├── .coveragerc ├── .github └── workflows │ └── checks.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .python-version ├── .readthedocs.yaml ├── .travis.yml ├── AUTHORS.rst ├── HISTORY.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── api.rst ├── conf.py ├── index.rst └── make.bat ├── fauxfactory ├── __init__.py ├── constants.py ├── factories │ ├── __init__.py │ ├── booleans.py │ ├── choices.py │ ├── dates.py │ ├── internet.py │ ├── numbers.py │ ├── strings.py │ └── systems.py ├── facts.json └── helpers.py ├── pyproject.toml ├── requirements-dev.lock ├── requirements.lock ├── tests ├── __init__.py ├── test_booleans.py ├── test_check_validation.py ├── test_choices.py ├── test_dates.py ├── test_datetime.py ├── test_dir.py ├── test_domain.py ├── test_emails.py ├── test_getattr.py ├── test_html.py ├── test_ipaddress.py ├── test_lorem_ipsum.py ├── test_macs.py ├── test_netmasks.py ├── test_numbers.py ├── test_strings.py ├── test_system_facts.py ├── test_system_helpers.py ├── test_time.py ├── test_urls.py ├── test_utils.py └── test_uuids.py └── uv.lock /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = fauxfactory 3 | 4 | [report] 5 | omit = 6 | */python?.?/* 7 | */site-packages/nose/* 8 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Fauxfactory Checks 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: ["3.9", "3.10", "3.11", "3.12"] 16 | 17 | steps: 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v3 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | - uses: actions/checkout@v4 23 | - name: Install UV 24 | uses: astral-sh/setup-uv@v5 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | uv sync 30 | - name: Run checks 31 | run: | 32 | make all 33 | - name: Upload Coverage to Codecov 34 | uses: codecov/codecov-action@v1 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | MANIFEST 22 | 23 | # Installer logs 24 | pip-log.txt 25 | 26 | # Unit test / coverage reports 27 | .coverage 28 | .tox 29 | nosetests.xml 30 | coverage.xml 31 | 32 | # Translations 33 | *.mo 34 | 35 | # Mr Developer 36 | .mr.developer.cfg 37 | .project 38 | .pydevproject 39 | 40 | # Documentation artifacts 41 | /docs/_build/ 42 | 43 | # Vim swap files 44 | *.swp 45 | .pytest_cache 46 | .idea 47 | pyvenv.cfg 48 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # configuration for pre-commit git hooks 2 | 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.5.0 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: end-of-file-fixer 9 | - id: debug-statements 10 | - repo: https://github.com/astral-sh/ruff-pre-commit 11 | # Ruff version. 12 | rev: v1.3.1 13 | hooks: 14 | # Run the linter. 15 | - id: ruff 16 | # Run the formatter. 17 | - id: ruff-format -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12.2 2 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "latest" 12 | # You can also specify other tool versions: 13 | # nodejs: "20" 14 | # rust: "1.70" 15 | # golang: "1.20" 16 | 17 | # Build documentation in the "docs/" directory with Sphinx 18 | sphinx: 19 | configuration: docs/conf.py 20 | # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs 21 | # builder: "dirhtml" 22 | # Fail on all warnings to avoid broken references 23 | # fail_on_warning: true 24 | 25 | # Optionally build your docs in additional formats such as PDF and ePub 26 | formats: all 27 | # - epub 28 | 29 | # Optional but recommended, declare the Python requirements required 30 | # to build your documentation 31 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 32 | python: 33 | install: 34 | - requirements: docs/requirements.txt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | sudo: false 3 | language: python 4 | python: 5 | - 3.9 6 | - 3.10 7 | - 3.11 8 | - 3.12 9 | 10 | install: 11 | - pip install -U pip 12 | - pip install -r requirements-optional.txt 13 | script: 14 | - make all 15 | after_success: 16 | - codecov 17 | notifications: 18 | email: 19 | on_success: always 20 | on_failure: always 21 | deploy: 22 | provider: pypi 23 | skip_existing: true 24 | skip_cleanup: true 25 | # mshriver owned 26 | user: mshriver 27 | distributions: sdist bdist_wheel 28 | password: 29 | secure: "Hy941hCWVzI61chaGA1n77tNhJK7eT0ePpzZCO2i9bWzoakhlzwUTaPNupFiFSfx1ao3CXo8fpBEY9czNNGgi8rK9w5wwZrGA87mp+ugWfWgqfDhmW83MKvutGvjqLwyAXwauTRcOMdFErnO2eZuXGYPltfj4hoQCZ5jku+XZts=" 30 | on: 31 | tags: true 32 | repo: omaciel/fauxfactory 33 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | FauxFactory is written and maintained by Og Maciel and various 5 | contributors. 6 | 7 | Development Lead 8 | ---------------- 9 | 10 | - Og Maciel `@omaciel `_ 11 | 12 | Contributors 13 | ------------ 14 | 15 | - Corey Welton `@cswiii `_ 16 | - Elyézer Rezende `@elyezer `_ 17 | - Gilad Shefer `@gshefer `_ 18 | - Jacob Callahan `@JacobCallahan `_ 19 | - James Laska `@jlaska `_ 20 | - Jefferson Fausto Vaz `@faustovaz `_ 21 | - Jeremy Audet `@Ichimonji10 `_ 22 | - Jonathan Edwards `@apense `_ 23 | - Kedar Bidarkar `@kbidarkar `_ 24 | - Pavel Zagalsky `@pavelzag `_ 25 | - Renzo Nuccitelli `@renzon `_ 26 | - Sachin Ghai `@sghai `_ 27 | - Milan Falešník `@mfalesni `_ 28 | - Nikhil Dhandre `@digitronik `_ 29 | - Mike Shriver `@mshriver `_ 30 | - Lisa Walker `@liwalker-rh `_ 31 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | Release History 4 | =============== 5 | 6 | 3.1.2 (2025-03-04) 7 | ------------------ 8 | 9 | - Add option to disable random seed. 10 | 11 | 3.1.1 (2024-03-26) 12 | ------------------ 13 | 14 | - Dropping support for Python 3.6, 3.7; added 3.11, 3.12. 15 | - Several code linting and smell checks. 16 | - Improve execution time of some key methods 17 | - Better selection for CJK characters. 18 | 19 | 3.1.0 (2020-11-10) 20 | ------------------ 21 | 22 | - Support static analysis and type annotation 23 | - CI dropped Python 3.5 24 | 25 | 3.0.6 (2019-07-30) 26 | ------------------ 27 | 28 | - Change travis deploy credentials to token 29 | 30 | 3.0.5 (2019-07-30) 31 | ------------------ 32 | 33 | - Update setuptools versioning, add travis deployment 34 | 35 | 3.0.4 (2019-07-30) 36 | ------------------ 37 | 38 | - Resolve flake failures in travis 39 | 40 | 3.0.3 (2019-07-30) 41 | ------------------ 42 | 43 | - Fixes for warnings on file resources 44 | 45 | 3.0.2 (2018-04-10) 46 | ------------------ 47 | 48 | - Really include facts.json to the package 49 | 50 | 3.0.1 (2018-04-10) 51 | ------------------ 52 | 53 | - Add facts.json to manifest (2217706, @m-bucher) 54 | 55 | 3.0.0 (2018-04-10) 56 | ------------------ 57 | 58 | - Make `gen_utf8` return optionally only BMP characters 59 | (6201b63) 60 | - Don't install tests into the binary distribution 61 | (b291873, @evgeni) 62 | - Use floor division operator in base_repr for Python 3 63 | compatibility (914178a, @gshefer) 64 | - New `gen_octagonal` and `gen_hexadecimal` methods added 65 | (57f5d17, @gshefer) 66 | 67 | 2.1.0 (2017-03-30) 68 | ------------------ 69 | 70 | - All methods now allow you to provide a callable which will be 71 | used to filter values being returned, the number of tries, and 72 | a default value to be returned if the filter cannot match the 73 | values being generated after the number of tries. (2a7523, @renzon) 74 | 75 | 2.0.9 (2016-01-12) 76 | ------------------ 77 | 78 | - Force randomness every time `random` is used to make sure 79 | that unique values are generated when running on multi-process 80 | environments, such as py.test with the pytest-xdist plugin. 81 | 82 | 2.0.8 (2015-09-18) 83 | ------------------ 84 | 85 | - Updated the `gen_mac` method to allow the generation of 86 | unicast/multicast and globally/locally MAC addresses. 87 | 88 | 2.0.7 (2015-05-28) 89 | ------------------ 90 | 91 | - Updated the `gen_ipaddr` method to allow the generation of IP 92 | addresses that start with a valid range prefix. (048715d, @mfalesni) 93 | 94 | 2.0.6 (2015-02-24) 95 | ------------------ 96 | 97 | - Added support for **Python 2.6**. 98 | - Cleaned up the MANIFEST file. 99 | 100 | 2.0.5 (2015-02-16) 101 | ------------------ 102 | 103 | - Improved the unicode letters generator to avoid returning control 104 | characters and other non-letter characters. 105 | 106 | 2.0.4 (2014-12-19) 107 | ------------------ 108 | 109 | - Altered `gen_integer` to properly check for long() on Python 2. 110 | 111 | 2.0.3 (2014-12-17) 112 | ------------------ 113 | 114 | - Dropped the class-wide FauxFactory deprecation warning. 115 | - Refactored the `deprecated` decorator function to comply with pylint 116 | and flake8. 117 | - Make gen_netmask verify function arguments. 118 | - Make `gen_netmask` raise a `ValueError` if `min_cidr` is less than 119 | 0 or `max_cidr` is greater than 32. Add tests for this boundary 120 | checking code. 121 | - Improvements to constants and documentation. 122 | 123 | 124 | 2.0.2 (2014-10-06) 125 | ------------------ 126 | 127 | - Added new netmask random generator. 128 | 129 | 2.0.1 (2014-09-30) 130 | ------------------ 131 | 132 | - Added a default length of 10 to all string generator functions. 133 | - Display deprecation warnings if ``FauxFactory`` and any of its 134 | functions are used, instructing the user to use the newer functions 135 | instead. 136 | 137 | 2.0.0 (2014-09-23) 138 | ------------------ 139 | 140 | - All generators are now stand-alone functions and can be imported 141 | directly from ``fauxfactory``. For example, ``from fauxfactory 142 | import gen_date`` 143 | - Renamed all generator functions to use the prefix "gen\_" instead of 144 | "generate\_". For example, ``generate_date`` is now ``gen_date``. 145 | - Backwards compatibility with version 1.x. 146 | - Polished documentation. 147 | 148 | 1.0.1 (2014-09-18) 149 | ------------------ 150 | 151 | - Updated ``generate_string`` to also accept ``html`` strings. 152 | 153 | 1.0.0 (2014-09-17) 154 | ------------------ 155 | 156 | - Added new method generate_html 157 | - Added new makefile 158 | 159 | 0.3.1 (2014-07-10) 160 | ------------------ 161 | 162 | - Check for sys.maxunicode when generating utf8 characters. 163 | 164 | 0.3.0 (2014-06-15) 165 | ------------------ 166 | 167 | - FauxFactory is now Python 3 compatible 168 | - Added new method generate_utf8 169 | 170 | 0.2.1 (2014-05-09) 171 | ------------------ 172 | 173 | - Fixed issue that prevented strings longer than the full length of 174 | LOREM_IPSUM_TEXT from being generated (Github Issue #16). 175 | 176 | 0.2.0 (2014-05-08) 177 | ------------------ 178 | 179 | - Added new 'Lorem Ipsum' generator. 180 | - Changed license from LGPL3+ to Apache 2.0 181 | 182 | 0.1.3 (2014-04-16) 183 | ------------------ 184 | 185 | - Updated character range for CJK generator to avoid generating 'tofu' 186 | characters. 187 | - Added Contributors section to README. 188 | - New `documentation 189 | `_ (@faustovaz) 190 | 191 | **Bugfixes:** 192 | 193 | - Fixed generate_string function (@faustovaz) 194 | 195 | 0.1.2 (2014-03-19) 196 | ------------------ 197 | 198 | **Bugfixes:** 199 | 200 | - Generators for ``email``, ``url`` and ``IP`` should return a unicode 201 | string. 202 | 203 | 0.1.1 (2014-03-17) 204 | ------------------ 205 | 206 | - Fixed formatting of README for better display on Pypi. 207 | 208 | 0.1.0 (2014-03-17) 209 | ------------------ 210 | 211 | - Initial Release. 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Og Maciel 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include HISTORY.rst 3 | include LICENSE 4 | include README.rst 5 | include fauxfactory/facts.json 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "Please use \`make ' where is one of:" 3 | @echo " docs-clean Remove documentation." 4 | @echo " docs-doctest Check code samples in the documentation." 5 | @echo " docs-html Compile documentation to HTML." 6 | @echo " format Run format8." 7 | @echo " lint Run format8 and pylint." 8 | @echo " test Run unit tests." 9 | @echo " test-all Run unit tests and doctests, measure coverage." 10 | 11 | all: clean lint test-all check clean docs-clean docs-html 12 | 13 | # Linting and formatting 14 | check: 15 | uvx ruff check . 16 | 17 | # Clean build artifacts 18 | clean: 19 | rm -rf build/ 20 | rm -rf dist/ 21 | rm -rf *.egg-info 22 | find . -type d -name __pycache__ -exec rm -rf {} + 23 | find . -type f -name "*.pyc" -delete 24 | find . -type f -name "*.pyo" -delete 25 | find . -type f -name "*.pyd" -delete 26 | 27 | docs-clean: 28 | cd docs && rm -rf _build/* 29 | 30 | docs-doctest: install-docs 31 | uv run --with '.[docs]' sphinx-build -b doctest -d docs/_build/doctrees docs/ docs/_build/doctest 32 | 33 | docs-html: install-docs 34 | uv run --with '.[docs]' sphinx-build -b html -d docs/_build/doctrees docs/ docs/_build/html 35 | 36 | format: 37 | uvx ruff format . 38 | 39 | lint: check format 40 | 41 | install-dev: 42 | uv pip install -e ".[dev]" 43 | 44 | install-docs: 45 | uv pip install -e ".[docs]" 46 | 47 | build: clean 48 | uv build 49 | 50 | 51 | publish: 52 | uv publish 53 | 54 | test: install-dev 55 | uv run --with '.[dev]' pytest -v 56 | 57 | test-all: install-dev 58 | uv run --with pytest-cov --with '.[dev]' pytest --cov-report term-missing --cov=fauxfactory 59 | 60 | .PHONY: help build check clean docs-clean docs-doctest docs-html format lint build publish test test-all 61 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | FauxFactory 2 | =========== 3 | 4 | .. image:: https://travis-ci.org/omaciel/fauxfactory.png?branch=master 5 | :target: https://travis-ci.org/omaciel/fauxfactory 6 | :alt: Build Status 7 | 8 | .. image:: https://img.shields.io/pypi/pyversions/fauxfactory.svg 9 | :target: https://pypi.python.org/pypi/fauxfactory 10 | :alt: Python Compatibility 11 | 12 | .. image:: https://badge.fury.io/py/fauxfactory.png 13 | :target: http://badge.fury.io/py/fauxfactory 14 | :alt: Current Version 15 | 16 | .. image:: https://img.shields.io/pypi/dm/fauxfactory.svg 17 | :target: https://crate.io/packages/fauxfactory/ 18 | :alt: Download Statistics 19 | 20 | .. image:: https://coveralls.io/repos/omaciel/fauxfactory/badge.png?branch=master 21 | :target: https://coveralls.io/r/omaciel/fauxfactory?branch=master 22 | :alt: Test Coverage 23 | 24 | .. image:: https://img.shields.io/pypi/l/fauxfactory.svg 25 | :target: https://pypi.python.org/pypi/fauxfactory/ 26 | :alt: License 27 | 28 | **FauxFactory** generates random data for your automated tests easily! 29 | 30 | There are times when you're writing tests for your application when you need to 31 | pass random, non-specific data to the areas you are testing. For these scenarios 32 | when all you need is a random string, numbers, dates, times, email address, IP, 33 | etc, then FauxFactory can help! 34 | 35 | The `full documentation 36 | `_ is available on 37 | ReadTheDocs. It can also be generated locally:: 38 | 39 | pip install -r requirements-optional.txt 40 | make docs-html 41 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/FauxFactory.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/FauxFactory.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/FauxFactory" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/FauxFactory" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | API Documentation 2 | ================= 3 | 4 | This is a complete list of available methods, along with details about them. 5 | 6 | :mod:`fauxfactory` 7 | ------------------ 8 | 9 | .. automodule:: fauxfactory 10 | 11 | :mod:`fauxfactory.factories.booleans` 12 | ------------------------------------- 13 | 14 | .. automodule:: fauxfactory.factories.booleans 15 | :members: 16 | 17 | :mod:`fauxfactory.factories.choices` 18 | ------------------------------------ 19 | 20 | .. automodule:: fauxfactory.factories.choices 21 | :members: 22 | 23 | :mod:`fauxfactory.constants` 24 | ---------------------------- 25 | 26 | .. automodule:: fauxfactory.constants 27 | :members: 28 | 29 | :mod:`fauxfactory.factories.dates` 30 | ---------------------------------- 31 | 32 | .. automodule:: fauxfactory.factories.dates 33 | :members: 34 | 35 | :mod:`fauxfactory.helpers` 36 | -------------------------- 37 | 38 | .. automodule:: fauxfactory.helpers 39 | :members: 40 | 41 | :mod:`fauxfactory.factories.internet` 42 | ------------------------------------- 43 | 44 | .. automodule:: fauxfactory.factories.internet 45 | :members: 46 | 47 | :mod:`fauxfactory.factories.numbers` 48 | ------------------------------------ 49 | 50 | .. automodule:: fauxfactory.factories.numbers 51 | :members: 52 | 53 | :mod:`fauxfactory.factories.strings` 54 | ------------------------------------ 55 | 56 | .. automodule:: fauxfactory.factories.strings 57 | :members: 58 | 59 | :mod:`fauxfactory.factories.systems` 60 | ------------------------------------ 61 | 62 | .. automodule:: fauxfactory.factories.systems 63 | :members: 64 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Sphinx documentation generator configuration file. 3 | 4 | The full set of configuration options is listed on the Sphinx website: 5 | http://sphinx-doc.org/config.html 6 | 7 | """ 8 | # FauxFactory documentation build configuration file, created by 9 | # sphinx-quickstart on Wed Apr 16 15:46:04 2014. 10 | # 11 | # This file is execfile()d with the current directory set to its 12 | # containing dir. 13 | # 14 | # Note that not all possible configuration values are present in this 15 | # autogenerated file. 16 | # 17 | # All configuration values have a default; values that are commented out 18 | # serve to show the default. 19 | 20 | import os 21 | import sys 22 | 23 | # pylint:disable=invalid-name 24 | 25 | # If extensions (or modules to document with autodoc) are in another directory, 26 | # add these directories to sys.path here. If the directory is relative to the 27 | # documentation root, use os.path.abspath to make it absolute, like shown here. 28 | sys.path.insert( 29 | 0, os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir)) 30 | ) 31 | 32 | # -- General configuration ------------------------------------------------ 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # needs_sphinx = '1.0' 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | "sphinx.ext.autodoc", 42 | "sphinx.ext.doctest", 43 | "sphinx.ext.viewcode", 44 | ] 45 | 46 | # Add any paths that contain templates here, relative to this directory. 47 | templates_path = ["_templates"] 48 | 49 | # The suffix of source filenames. 50 | source_suffix = ".rst" 51 | 52 | # The encoding of source files. 53 | # source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = "index" 57 | 58 | # General information about the project. 59 | project = "FauxFactory" 60 | copyright = "2014-2025, Og Maciel" # pylint:disable=redefined-builtin 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = "3.1" 68 | # The full version, including alpha/beta/rc tags. 69 | release = "3.1.2" 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # language = None 74 | 75 | # There are two options for replacing |today|: either, you set today to some 76 | # non-false value, then it is used: 77 | # today = '' 78 | # Else, today_fmt is used as the format for a strftime call. 79 | # today_fmt = '%B %d, %Y' 80 | 81 | # List of patterns, relative to source directory, that match files and 82 | # directories to ignore when looking for source files. 83 | exclude_patterns = ["_build"] 84 | 85 | # The reST default role (used for this markup: `text`) to use for all 86 | # documents. 87 | # default_role = None 88 | 89 | # If true, '()' will be appended to :func: etc. cross-reference text. 90 | # add_function_parentheses = True 91 | 92 | # If true, the current module name will be prepended to all description 93 | # unit titles (such as .. function::). 94 | # add_module_names = True 95 | 96 | # If true, sectionauthor and moduleauthor directives will be shown in the 97 | # output. They are ignored by default. 98 | # show_authors = False 99 | 100 | # The name of the Pygments (syntax highlighting) style to use. 101 | pygments_style = "sphinx" 102 | 103 | # A list of ignored prefixes for module index sorting. 104 | # modindex_common_prefix = [] 105 | 106 | # If true, keep warnings as "system message" paragraphs in the built documents. 107 | # keep_warnings = False 108 | 109 | # If true, Sphinx will warn about all references where the target cannot be 110 | # found. 111 | nitpicky = True 112 | 113 | # A list of (type, target) tuples (by default empty) that should be ignored 114 | # when generating warnings in “nitpicky mode”. 115 | nitpick_ignore = [ 116 | ("py:class", "bool"), 117 | ("py:class", "dict"), 118 | ("py:class", "int"), 119 | ("py:class", "list"), 120 | ("py:class", "str"), 121 | ("py:class", "tuple"), 122 | ] 123 | 124 | 125 | # -- Options for HTML output ---------------------------------------------- 126 | 127 | # The theme to use for HTML and HTML Help pages. See the documentation for 128 | # a list of builtin themes. 129 | html_theme = "nature" 130 | 131 | # Theme options are theme-specific and customize the look and feel of a theme 132 | # further. For a list of options available for each theme, see the 133 | # documentation. 134 | # html_theme_options = {} 135 | 136 | # Add any paths that contain custom themes here, relative to this directory. 137 | # html_theme_path = [] 138 | 139 | # The name for this set of Sphinx documents. If None, it defaults to 140 | # " v documentation". 141 | # html_title = None 142 | 143 | # A shorter title for the navigation bar. Default is the same as html_title. 144 | # html_short_title = None 145 | 146 | # The name of an image file (relative to this directory) to place at the top 147 | # of the sidebar. 148 | # html_logo = None 149 | 150 | # The name of an image file (within the static path) to use as favicon of the 151 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 152 | # pixels large. 153 | # html_favicon = None 154 | 155 | # Add any paths that contain custom static files (such as style sheets) here, 156 | # relative to this directory. They are copied after the builtin static files, 157 | # so a file named "default.css" will overwrite the builtin "default.css". 158 | # html_static_path = ['_static'] 159 | 160 | # Add any extra paths that contain custom files (such as robots.txt or 161 | # .htaccess) here, relative to this directory. These files are copied 162 | # directly to the root of the documentation. 163 | # html_extra_path = [] 164 | 165 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 166 | # using the given strftime format. 167 | # html_last_updated_fmt = '%b %d, %Y' 168 | 169 | # If true, SmartyPants will be used to convert quotes and dashes to 170 | # typographically correct entities. 171 | # html_use_smartypants = True 172 | 173 | # Custom sidebar templates, maps document names to template names. 174 | # html_sidebars = {} 175 | 176 | # Additional templates that should be rendered to pages, maps page names to 177 | # template names. 178 | # html_additional_pages = {} 179 | 180 | # If false, no module index is generated. 181 | # html_domain_indices = True 182 | 183 | # If false, no index is generated. 184 | # html_use_index = True 185 | 186 | # If true, the index is split into individual pages for each letter. 187 | # html_split_index = False 188 | 189 | # If true, links to the reST sources are added to the pages. 190 | # html_show_sourcelink = True 191 | 192 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 193 | # html_show_sphinx = True 194 | 195 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 196 | # html_show_copyright = True 197 | 198 | # If true, an OpenSearch description file will be output, and all pages will 199 | # contain a tag referring to it. The value of this option must be the 200 | # base URL from which the finished HTML is served. 201 | # html_use_opensearch = '' 202 | 203 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 204 | # html_file_suffix = None 205 | 206 | # Output file base name for HTML help builder. 207 | htmlhelp_basename = "FauxFactorydoc" 208 | 209 | 210 | # -- Options for LaTeX output --------------------------------------------- 211 | 212 | latex_elements = { 213 | # The paper size ('letterpaper' or 'a4paper'). 214 | # 'papersize': 'letterpaper', 215 | # The font size ('10pt', '11pt' or '12pt'). 216 | # 'pointsize': '10pt', 217 | # Additional stuff for the LaTeX preamble. 218 | # 'preamble': '', 219 | } 220 | 221 | # Grouping the document tree into LaTeX files. List of tuples 222 | # (source start file, target name, title, 223 | # author, documentclass [howto, manual, or own class]). 224 | latex_documents = [ 225 | ("index", "FauxFactory.tex", "FauxFactory Documentation", "Og Maciel", "manual") 226 | ] 227 | 228 | # The name of an image file (relative to this directory) to place at the top of 229 | # the title page. 230 | # latex_logo = None 231 | 232 | # For "manual" documents, if this is true, then toplevel headings are parts, 233 | # not chapters. 234 | # latex_use_parts = False 235 | 236 | # If true, show page references after internal links. 237 | # latex_show_pagerefs = False 238 | 239 | # If true, show URL addresses after external links. 240 | # latex_show_urls = False 241 | 242 | # Documents to append as an appendix to all manuals. 243 | # latex_appendices = [] 244 | 245 | # If false, no module index is generated. 246 | # latex_domain_indices = True 247 | 248 | 249 | # -- Options for manual page output --------------------------------------- 250 | 251 | # One entry per manual page. List of tuples 252 | # (source start file, name, description, authors, manual section). 253 | man_pages = [("index", "fauxfactory", "FauxFactory Documentation", ["Og Maciel"], 1)] 254 | 255 | # If true, show URL addresses after external links. 256 | # man_show_urls = False 257 | 258 | 259 | # -- Options for Texinfo output ------------------------------------------- 260 | 261 | # Grouping the document tree into Texinfo files. List of tuples 262 | # (source start file, target name, title, author, 263 | # dir menu entry, description, category) 264 | texinfo_documents = [ 265 | ( 266 | "index", 267 | "FauxFactory", 268 | "FauxFactory Documentation", 269 | "Og Maciel", 270 | "FauxFactory", 271 | "FauxFactory generates random data for your automated tests easily!", 272 | "Miscellaneous", 273 | ) 274 | ] 275 | 276 | # Documents to append as an appendix to all manuals. 277 | # texinfo_appendices = [] 278 | 279 | # If false, no module index is generated. 280 | # texinfo_domain_indices = True 281 | 282 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 283 | # texinfo_show_urls = 'footnote' 284 | 285 | # If true, do not generate a @detailmenu in the "Top" node's menu. 286 | # texinfo_no_detailmenu = False 287 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | FauxFactory 2 | =========== 3 | 4 | **FauxFactory** generates random data for your automated tests easily! 5 | 6 | There are times when you're writing tests for your application when you need to 7 | pass random, non-specific data to the areas you are testing. For these scenarios 8 | when all you need is a random string, numbers, dates, times, email address, IP, 9 | etc, then FauxFactory can help! 10 | 11 | Installation 12 | ------------ 13 | 14 | FauxFactory is available in PyPi and can be installed using pip:: 15 | 16 | $ pip install fauxfactory 17 | 18 | You can install FauxFactory by downloading the latest version of the source 19 | code:: 20 | 21 | $ git clone git@github.com:omaciel/fauxfactory.git 22 | $ cd fauxfactory 23 | $ python setup.py build install 24 | 25 | Usage 26 | ----- 27 | 28 | .. testsetup:: 29 | 30 | import os 31 | import sys 32 | ROOT_PATH = os.path.abspath(os.path.pardir) 33 | if ROOT_PATH not in sys.path: 34 | sys.path.append(ROOT_PATH) 35 | import fauxfactory 36 | 37 | Need a 15 character string for one of your tests? 38 | 39 | .. doctest:: 40 | 41 | >>> string = fauxfactory.gen_string('alphanumeric', 15) 42 | >>> string.isalnum() 43 | True 44 | >>> len(string) 45 | 15 46 | 47 | Need a 5 character numeric string? 48 | 49 | .. doctest:: 50 | 51 | >>> string = fauxfactory.gen_string('numeric', 5) 52 | >>> string.isnumeric() 53 | True 54 | >>> len(string) 55 | 5 56 | 57 | Now, let's say you need a random date: 58 | 59 | .. doctest:: 60 | 61 | >>> import datetime 62 | >>> isinstance(fauxfactory.gen_date(), datetime.date) 63 | True 64 | >>> isinstance(fauxfactory.gen_datetime(), datetime.datetime) 65 | True 66 | 67 | Or a fake email with your company domain: 68 | 69 | .. doctest:: 70 | 71 | >>> email = fauxfactory.gen_email(domain='mycompany') 72 | >>> '@mycompany' in email 73 | True 74 | 75 | Simple, right? 76 | 77 | Validation 78 | ---------- 79 | 80 | All string functions allow validation of inputs using 3 parameters: 81 | 82 | #. validator: a callable or str with regex returning boolean signaling if 83 | random data is valid or not. 84 | #. tries: maximum number of times random data will be generated after 85 | failing validation. If the limit is reached "default" parameter will be 86 | returned. 87 | #. default: value to be returned if validation fails a "tries" number of times. 88 | 89 | 90 | Example using callable: 91 | 92 | .. doctest:: 93 | 94 | >>> def start_a(value): 95 | ... return value[0] == 'a' 96 | >>> email = fauxfactory.gen_email(validator=start_a, default = 'a@b.c') 97 | >>> email[0] == 'a' 98 | True 99 | 100 | 101 | Example using regex: 102 | 103 | .. doctest:: 104 | 105 | >>> n = fauxfactory.gen_string( 106 | ... 'numeric', validator='[^0].*', default = '2') 107 | >>> n != '0' 108 | True 109 | 110 | Example using tries and default: 111 | 112 | .. doctest:: 113 | 114 | >>> def always_false(value): 115 | ... print('Executed') 116 | ... return False 117 | >>> fauxfactory.gen_alpha( 118 | ... validator=always_false, default = 'default value', tries=1) 119 | Executed 120 | 'default value' 121 | >>> fauxfactory.gen_alpha( 122 | ... validator=always_false, default = 'default value 2', tries=3) 123 | Executed 124 | Executed 125 | Executed 126 | 'default value 2' 127 | 128 | API 129 | --- 130 | 131 | For a full list of available methods, see the :doc:`API documentation <./api>`. 132 | 133 | .. toctree:: 134 | :hidden: 135 | 136 | api 137 | 138 | Contribute 139 | ---------- 140 | 141 | #. Fork `the repository`_ on GitHub and make some changes. Make sure to add 142 | yourself to `AUTHORS`_. 143 | #. Install the development requirements. ``pip install -r 144 | requirements-optional.txt``. 145 | #. Test your changes. 146 | 147 | #. Run ``make test-all`` and make sure nothing has broken. 148 | #. Run ``coverage report --show-missing`` to check for untested code. 149 | #. Add tests to the ``tests/`` directory if appropriate. 150 | 151 | Repeat this cycle as needed. 152 | 153 | #. Send a pull request and bug the maintainer until it gets merged and 154 | published. :) 155 | 156 | .. _`the repository`: http://github.com/omaciel/fauxfactory 157 | .. _`AUTHORS`: https://github.com/omaciel/fauxfactory/blob/master/AUTHORS.rst 158 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\FauxFactory.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\FauxFactory.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /fauxfactory/__init__.py: -------------------------------------------------------------------------------- 1 | """Generate random data for your tests.""" 2 | 3 | # Note flake8 do not like star imports. It makes tracking down the file/module 4 | # containg the symbol much harder than with explicit imports. This is an issue 5 | # especially for a human reader. As symbols in this file were always taken from 6 | # submodules dynamically this problem was there before introducing the start 7 | # imports. Star imports seem to not be a problem for (perhaps) smarter tools 8 | # like PyCharm and using them improves the analysis and navigation trough 9 | # the code. 10 | 11 | from fauxfactory.factories.booleans import * # noqa: F401, F403 12 | from fauxfactory.factories.choices import * # noqa: F401, F403 13 | from fauxfactory.factories.dates import * # noqa: F401, F403 14 | from fauxfactory.factories.internet import * # noqa: F401, F403 15 | from fauxfactory.factories.numbers import * # noqa: F401, F403 16 | from fauxfactory.factories.strings import * # noqa: F401, F403 17 | from fauxfactory.factories.systems import * # noqa: F401, F403 18 | 19 | __factories = {name: obj for name, obj in locals().items() if name.startswith("gen_")} 20 | 21 | # Add all method names to __all__ 22 | __all__ = tuple(__factories.keys()) 23 | 24 | 25 | def __dir__(): 26 | return __all__ 27 | 28 | 29 | def __getattr__(name): 30 | if name in __factories: 31 | return __factories[name] 32 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 33 | -------------------------------------------------------------------------------- /fauxfactory/constants.py: -------------------------------------------------------------------------------- 1 | """Constants used by :mod:`fauxfactory`. 2 | 3 | .. data:: VALID_NETMASKS 4 | 5 | A tuple of netmasks. The tuple index corresponds to a CIDR value. For 6 | example, a CIDR of "/1" corresponds to `VALID_NETMASKS[1]`. 7 | 8 | """ 9 | 10 | # The above constant descriptions can be found by Sphinx and via help(). 11 | import datetime 12 | import json 13 | import os 14 | import string 15 | 16 | VALID_DIGITS = string.digits + string.ascii_letters 17 | 18 | FACTS_JSON_FILE = os.path.join(os.path.dirname(__file__), "facts.json") 19 | with open(FACTS_JSON_FILE, encoding="utf-8") as data: 20 | FACTS_JSON = json.load(data) 21 | 22 | LOREM_IPSUM_TEXT = ( 23 | "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " 24 | "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad " 25 | "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " 26 | "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in " 27 | "voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur " 28 | "sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " 29 | "mollit anim id est laborum." 30 | ) 31 | 32 | MIN_YEARS = datetime.MINYEAR 33 | MAX_YEARS = 1000 # 1000 years into the future 34 | 35 | SCHEMES = ( 36 | "http", 37 | "https", 38 | "ftp", 39 | ) 40 | 41 | SUBDOMAINS = ( 42 | "example", 43 | "test", 44 | ) 45 | 46 | TLDS = ( 47 | "biz", 48 | "com", 49 | "edu", 50 | "gov", 51 | "info", 52 | "org", 53 | ) 54 | 55 | VALID_NETMASKS = ( 56 | "0.0.0.0", 57 | "128.0.0.0", 58 | "192.0.0.0", 59 | "224.0.0.0", 60 | "240.0.0.0", 61 | "248.0.0.0", 62 | "252.0.0.0", 63 | "254.0.0.0", 64 | "255.0.0.0", 65 | "255.128.0.0", 66 | "255.192.0.0", 67 | "255.224.0.0", 68 | "255.240.0.0", 69 | "255.248.0.0", 70 | "255.252.0.0", 71 | "255.254.0.0", 72 | "255.255.0.0", 73 | "255.255.128.0", 74 | "255.255.192.0", 75 | "255.255.224.0", 76 | "255.255.240.0", 77 | "255.255.248.0", 78 | "255.255.252.0", 79 | "255.255.254.0", 80 | "255.255.255.0", 81 | "255.255.255.128", 82 | "255.255.255.192", 83 | "255.255.255.224", 84 | "255.255.255.240", 85 | "255.255.255.248", 86 | "255.255.255.252", 87 | "255.255.255.254", 88 | "255.255.255.255", 89 | ) 90 | 91 | HTML_TAGS = ( 92 | "a", 93 | "abbr", 94 | "acronym", 95 | "address", 96 | "applet", 97 | "area", 98 | "b", 99 | "base", 100 | "basefont", 101 | "bdo", 102 | "big", 103 | "blink", 104 | "blockquote", 105 | "body", 106 | "br", 107 | "button", 108 | "caption", 109 | "center", 110 | "cite", 111 | "code", 112 | "col", 113 | "colgroup", 114 | "dd", 115 | "del", 116 | "dfn", 117 | "dir", 118 | "div", 119 | "dl", 120 | "dt", 121 | "em", 122 | "fieldset", 123 | "font", 124 | "form", 125 | "frame", 126 | "frameset", 127 | "h1", 128 | "h2", 129 | "h3", 130 | "h4", 131 | "h5", 132 | "h6", 133 | "head", 134 | "hr", 135 | "html", 136 | "i", 137 | "iframe", 138 | "img", 139 | "input", 140 | "ins", 141 | "isindex", 142 | "kbd", 143 | "label", 144 | "legend", 145 | "li", 146 | "link", 147 | "map", 148 | "menu", 149 | "meta", 150 | "noframes", 151 | "noscript", 152 | "object", 153 | "ol", 154 | "optgroup", 155 | "option", 156 | "p", 157 | "param", 158 | "pre", 159 | "q", 160 | "s", 161 | "samp", 162 | "script", 163 | "select", 164 | "small", 165 | "span", 166 | "strike", 167 | "strong", 168 | "style", 169 | "sub", 170 | "sup", 171 | "table", 172 | "tbody", 173 | "td", 174 | "textarea", 175 | "tfoot", 176 | "th", 177 | "thead", 178 | "title", 179 | "tr", 180 | "tt", 181 | "u", 182 | "ul", 183 | "var", 184 | ) 185 | -------------------------------------------------------------------------------- /fauxfactory/factories/__init__.py: -------------------------------------------------------------------------------- 1 | """Store all modules that generate data here.""" 2 | 3 | import os 4 | import random 5 | 6 | if "FAUXFACTORY_DISABLE_SEED_RANDOMIZATION" not in os.environ: 7 | random.seed() 8 | -------------------------------------------------------------------------------- /fauxfactory/factories/booleans.py: -------------------------------------------------------------------------------- 1 | """Method for generating random boolean values.""" 2 | 3 | from .choices import gen_choice 4 | 5 | 6 | def gen_boolean(): 7 | """Return a random Boolean value. 8 | 9 | :returns: A random Boolean value. 10 | :rtype: bool 11 | 12 | """ 13 | choices = (True, False) 14 | 15 | return gen_choice(choices) 16 | 17 | 18 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 19 | -------------------------------------------------------------------------------- /fauxfactory/factories/choices.py: -------------------------------------------------------------------------------- 1 | """Module to keep methods related to selecting values.""" 2 | 3 | import random 4 | import uuid 5 | from collections.abc import Iterable 6 | 7 | from fauxfactory.helpers import check_validation 8 | 9 | 10 | def gen_choice(choices): 11 | """Return a random choice from the available choices. 12 | 13 | :param list choices: List of choices from which select a random value. 14 | :raises: ``ValueError`` if ``choices`` is ``None`` or not ``Iterable`` or 15 | a ``dict``. 16 | :returns: A random element from ``choices``. 17 | 18 | """ 19 | # Validation for 'choices' 20 | if choices is None: 21 | raise ValueError("Choices argument cannot be None.") 22 | # We don't want a single dictionary value. 23 | if not isinstance(choices, Iterable) or isinstance(choices, dict): 24 | raise ValueError("Choices argument is not iterable.") 25 | if not choices: 26 | raise ValueError("Choices argument cannot be empty.") 27 | # If only 1 item is present, return it right away 28 | if len(choices) == 1: 29 | return choices[0] 30 | 31 | return random.choice(choices) 32 | 33 | 34 | @check_validation 35 | def gen_uuid(): 36 | """Generate a UUID string (universally unique identifiers). 37 | 38 | :returns: Returns a string representation for a UUID. 39 | :rtype: str 40 | 41 | """ 42 | return str(uuid.uuid4()) 43 | 44 | 45 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 46 | 47 | 48 | def __dir__(): 49 | return __all__ 50 | -------------------------------------------------------------------------------- /fauxfactory/factories/dates.py: -------------------------------------------------------------------------------- 1 | """Methods related to generating date/time related values.""" 2 | 3 | import datetime 4 | import random 5 | 6 | from fauxfactory.constants import MAX_YEARS, MIN_YEARS 7 | 8 | 9 | def gen_date(min_date=None, max_date=None): 10 | """Return a random date value. 11 | 12 | :param min_date: A valid ``datetime.date`` object. 13 | :param max_date: A valid ``datetime.date`` object. 14 | :raises: ``ValueError`` if arguments are not valid ``datetime.date`` 15 | objects. 16 | :returns: Random ``datetime.date`` object. 17 | 18 | """ 19 | _min_value = datetime.date.today() - datetime.timedelta(365 * MIN_YEARS) 20 | _max_value = datetime.date.today() + datetime.timedelta(365 * MAX_YEARS) 21 | 22 | if min_date is None: 23 | min_date = _min_value 24 | if max_date is None: 25 | max_date = _max_value 26 | 27 | # Validation 28 | if not isinstance(min_date, datetime.date): 29 | raise ValueError("%s is not a valid datetime.date object") 30 | if not isinstance(max_date, datetime.date): 31 | raise ValueError("%s is not a valid datetime.date object") 32 | 33 | # Check that max_date is not before min_date 34 | assert min_date < max_date 35 | 36 | # Pick a day between min and max dates 37 | diff = max_date - min_date 38 | days = random.randint(0, diff.days) 39 | date = min_date + datetime.timedelta(days=days) 40 | 41 | return date 42 | 43 | 44 | def gen_datetime(min_date=None, max_date=None): 45 | """Return a random datetime value. 46 | 47 | :param min_date: A valid ``datetime.datetime`` object. 48 | :param max_date: A valid ``datetime.datetime`` object. 49 | :raises: ``ValueError`` if arguments are not valid ``datetime.datetime`` 50 | objects. 51 | :returns: Random ``datetime.datetime`` object. 52 | 53 | """ 54 | _min_value = datetime.datetime.now() - datetime.timedelta(365 * MIN_YEARS) 55 | _max_value = datetime.datetime.now() + datetime.timedelta(365 * MAX_YEARS) 56 | 57 | if min_date is None: 58 | min_date = _min_value 59 | if max_date is None: 60 | max_date = _max_value 61 | 62 | # Validation 63 | if not isinstance(min_date, datetime.datetime): 64 | raise ValueError("%s is not a valid datetime.datetime object") 65 | if not isinstance(max_date, datetime.datetime): 66 | raise ValueError("%s is not a valid datetime.datetime object") 67 | 68 | # Check that max_date is not before min_date 69 | assert min_date < max_date 70 | 71 | # Pick a time between min and max dates 72 | diff = max_date - min_date 73 | seconds = random.randint(0, diff.days * 3600 * 24 + diff.seconds) 74 | 75 | return min_date + datetime.timedelta(seconds=seconds) 76 | 77 | 78 | def gen_time(): 79 | """Generate a random time. 80 | 81 | :returns: A random ``datetime.time`` object. 82 | 83 | """ 84 | return datetime.time( 85 | random.randint(0, 23), 86 | random.randint(0, 59), 87 | random.randint(0, 59), 88 | random.randint(0, 999999), 89 | ) 90 | 91 | 92 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 93 | 94 | 95 | def __dir__(): 96 | return __all__ 97 | -------------------------------------------------------------------------------- /fauxfactory/factories/internet.py: -------------------------------------------------------------------------------- 1 | """Methods related to generating internet related values.""" 2 | 3 | import random 4 | import re 5 | 6 | from fauxfactory.constants import SCHEMES, SUBDOMAINS, TLDS, VALID_NETMASKS 7 | from fauxfactory.helpers import check_validation 8 | 9 | from .choices import gen_choice 10 | from .strings import gen_alpha 11 | 12 | 13 | def gen_domain(name=None, subdomain=None, tlds=None): 14 | """Generate a random domain name. 15 | 16 | :param str name: Name for your host. 17 | :param str subdomain: Name for the subdomain. 18 | :param str tlds: Top Level Domain Server. 19 | :returns: A random domain name. 20 | :rtype: str 21 | 22 | """ 23 | # Generate a new name if needed 24 | if name is None: 25 | name = gen_alpha(8).lower() 26 | # Obtain a random subdomain if needed 27 | if subdomain is None: 28 | subdomain = gen_choice(SUBDOMAINS) 29 | # Obtain a random top level domain if needed 30 | if tlds is None: 31 | tlds = gen_choice(TLDS) 32 | 33 | return f"{name}.{subdomain}.{tlds}" 34 | 35 | 36 | @check_validation 37 | def gen_email(name=None, domain=None, tlds=None): 38 | """Generate a random email address. 39 | 40 | :param str name: Email name. 41 | :param str domain: Domain name. 42 | :param str tlds: Top Level Domain Server. 43 | :returns: An email address. 44 | :rtype: str 45 | 46 | """ 47 | # Generate a new name if needed 48 | if name is None: 49 | name = gen_alpha(8) 50 | # Obtain a random domain if needed 51 | if domain is None: 52 | domain = gen_choice(SUBDOMAINS) 53 | # Obtain a random top level domain if needed 54 | if tlds is None: 55 | tlds = gen_choice(TLDS) 56 | 57 | email = f"{name}@{domain}.{tlds}" 58 | 59 | return email 60 | 61 | 62 | @check_validation 63 | def gen_ipaddr(ip3=False, ipv6=False, prefix=()): 64 | """Generate a random IP address. 65 | 66 | You can also specify an IP address prefix if you are interested in 67 | local network address generation, etc. 68 | 69 | :param bool ip3: Whether to generate a 3 or 4 group IP. 70 | :param bool ipv6: Whether to generate IPv6 or IPv4 71 | :param list prefix: A prefix to be used for an IP (e.g. [10, 0, 1]). It 72 | must be an iterable with strings or integers. Can be left 73 | unspecified or empty. 74 | :returns: An IP address. 75 | :rtype: str 76 | :raises: ``ValueError`` if ``prefix`` would lead to no random fields at 77 | all. This means the length that triggers the ``ValueError`` is 4 for 78 | regular IPv4, 3 for IPv4 with ip3 and 8 for IPv6. It will be raised in 79 | any case the prefix length reaches or exceeds those values. 80 | 81 | """ 82 | # Set the lengths of the randomly generated sections 83 | if ipv6: 84 | rng = 8 85 | elif ip3: 86 | rng = 3 87 | else: 88 | rng = 4 89 | prefix = [str(field) for field in prefix] 90 | # Prefix reduces number of random fields generated, so subtract the length 91 | # of it from the rng to keep the IP address have correct number of fields 92 | rng -= len(prefix) 93 | if rng == 0: 94 | raise ValueError(f"Prefix {prefix!r} would lead to no randomness at all") 95 | if rng < 0: 96 | raise ValueError(f"Prefix {prefix!r} is too long for this configuration") 97 | 98 | if ipv6: 99 | # StackOverflow.com questions: generate-random-ipv6-address 100 | random_fields = [f"{random.randint(0, 2**16 - 1):x}" for _ in range(rng)] 101 | ipaddr = ":".join(prefix + random_fields) 102 | else: 103 | random_fields = [str(random.randrange(0, 255, 1)) for _ in range(rng)] 104 | ipaddr = ".".join(prefix + random_fields) 105 | 106 | if ip3: 107 | ipaddr = ipaddr + ".0" 108 | 109 | return ipaddr 110 | 111 | 112 | @check_validation 113 | def gen_mac(delimiter=":", multicast=None, locally=None): 114 | """Generate a random MAC address. 115 | 116 | For more information about how unicast or multicast and globally unique and 117 | locally administered MAC addresses are generated check this link 118 | https://en.wikipedia.org/wiki/MAC_address. 119 | 120 | :param str delimiter: Valid MAC delimiter (e.g ':', '-'). 121 | :param bool multicast: Indicates if the generated MAC address should be 122 | unicast or multicast. If no value is provided a random one will be 123 | chosen. 124 | :param bool locally: Indicates if the generated MAC address should be 125 | globally unique or locally administered. If no value is provided a 126 | random one will be chosen. 127 | :returns: A random MAC address. 128 | :rtype: str 129 | 130 | """ 131 | if delimiter not in [":", "-"]: 132 | raise ValueError(f"Delimiter is not a valid option: {delimiter}") 133 | if multicast is None: 134 | multicast = bool(random.randint(0, 1)) 135 | if locally is None: 136 | locally = bool(random.randint(0, 1)) 137 | 138 | first_octet = random.randint(0, 255) 139 | if multicast: 140 | # Ensure that the first least significant bit is 1 141 | first_octet |= 0b00000001 142 | else: 143 | # Ensure that the first least significant bit is 0 144 | first_octet &= 0b11111110 145 | if locally: 146 | # Ensure that the second least significant bit is 1 147 | first_octet |= 0b00000010 148 | else: 149 | # Ensure that the second least significant bit is 0 150 | first_octet &= 0b11111101 151 | 152 | octets = [first_octet] 153 | octets.extend(random.randint(0, 255) for _ in range(5)) 154 | mac = delimiter.join([f"{octet:02x}" for octet in octets]) 155 | 156 | return mac 157 | 158 | 159 | @check_validation 160 | def gen_netmask(min_cidr=1, max_cidr=31): 161 | """Generate a random valid netmask. 162 | 163 | For more info: http://www.iplocation.net/tools/netmask.php 164 | 165 | :param int min_cidr: Inferior CIDR limit 166 | :param int max_cidr: Superior CIDR limit 167 | :returns: The netmask is chosen from 168 | :data:`fauxfactory.constants.VALID_NETMASKS` respecting the CIDR range 169 | :rtype: str 170 | :raises: ``ValueError`` if ``min_cidr`` or ``max_cidr`` have an invalid 171 | value. For example, ``max_cidr`` cannot be 33. 172 | 173 | """ 174 | if min_cidr < 0: 175 | raise ValueError(f"min_cidr must be 0 or greater, but is {min_cidr}") 176 | if max_cidr >= len(VALID_NETMASKS): 177 | raise ValueError( 178 | f"max_cidr must be less than {len(VALID_NETMASKS)}, but is {max_cidr}" 179 | ) 180 | return VALID_NETMASKS[random.randint(min_cidr, max_cidr)] 181 | 182 | 183 | @check_validation 184 | def gen_url(scheme=None, subdomain=None, tlds=None): 185 | """Generate a random URL address. 186 | 187 | :param str scheme: Either http, https or ftp. 188 | :param str subdomain: A valid subdomain 189 | :param str tlds: A qualified top level domain name (e.g. 'com', 'net') 190 | :raises: ``ValueError`` if arguments are not valid. 191 | :returns: A random URL address. 192 | :rtype: str 193 | 194 | """ 195 | # Regex for subdomain names 196 | subdomainator = re.compile(r"^[a-zA-Z0-9][-\w.~]*$") 197 | # Regex for URL scheme 198 | schemenator = re.compile(r"^(https?|ftp)$") 199 | # Regex for TLDS 200 | tldsnator = re.compile(r"^[a-zA-Z]{1,3}$") 201 | 202 | if scheme: 203 | if schemenator.match(scheme) is None: 204 | raise ValueError(f"Protocol {scheme} is not valid.") 205 | else: 206 | scheme = gen_choice(SCHEMES) 207 | 208 | if subdomain: 209 | if subdomainator.match(subdomain) is None: 210 | raise ValueError(f"Subdomain {subdomain} is invalid.") 211 | else: 212 | subdomain = gen_choice(SUBDOMAINS) 213 | 214 | if tlds: 215 | if tldsnator.match(tlds) is None: 216 | raise ValueError(f"TLDS name {tlds} is invalid.") 217 | else: 218 | tlds = gen_choice(TLDS) 219 | 220 | url = f"{scheme}://{subdomain}.{tlds}" 221 | 222 | return url 223 | 224 | 225 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 226 | 227 | 228 | def __dir__(): 229 | return __all__ 230 | -------------------------------------------------------------------------------- /fauxfactory/factories/numbers.py: -------------------------------------------------------------------------------- 1 | """Methods that generate random number values.""" 2 | 3 | import random 4 | import sys 5 | from functools import partial 6 | 7 | from fauxfactory.helpers import base_repr 8 | 9 | 10 | def gen_integer(min_value=None, max_value=None): 11 | """Return a random integer value based on the current platform. 12 | 13 | :param int min_value: The minimum allowed value. 14 | :param int max_value: The maximum allowed value. 15 | :raises: ``ValueError`` if arguments are not integers or if they are 16 | less or greater than the system's allowed range for integers. 17 | :returns: Returns a random integer value. 18 | :rtype: int 19 | 20 | """ 21 | # Platform-specific value range for integers 22 | _min_value = -sys.maxsize - 1 23 | _max_value = sys.maxsize 24 | 25 | if min_value is None: 26 | min_value = _min_value 27 | if max_value is None: 28 | max_value = _max_value 29 | 30 | integer_types = (int,) 31 | 32 | # Perform some validations 33 | if not isinstance(min_value, integer_types) or min_value < _min_value: 34 | raise ValueError(f'"{min_value}" is not a valid minimum.') 35 | if not isinstance(max_value, integer_types) or max_value > _max_value: 36 | raise ValueError(f'"{max_value}" is not a valid maximum.') 37 | 38 | value = random.randint(min_value, max_value) 39 | 40 | return value 41 | 42 | 43 | def gen_negative_integer(): 44 | """Return a random negative integer based on the current platform. 45 | 46 | :returns: Returns a random negative integer value. 47 | :rtype: int 48 | 49 | """ 50 | max_value = 0 51 | 52 | return gen_integer(max_value=max_value) 53 | 54 | 55 | def gen_positive_integer(): 56 | """Return a random positive integer based on the current platform. 57 | 58 | :returns: A random positive integer value. 59 | :rtype: int 60 | 61 | """ 62 | min_value = 0 63 | 64 | return gen_integer(min_value=min_value) 65 | 66 | 67 | def gen_number(min_value=None, max_value=None, base=10): 68 | """Return a random number (with representation). 69 | 70 | :returns: A random number with base of . 71 | :rtype: str 72 | 73 | """ 74 | return base_repr( 75 | gen_integer( 76 | int(str(min_value), base=base) if min_value is not None else min_value, 77 | int(str(max_value), base=base) if max_value is not None else max_value, 78 | ), 79 | base, 80 | ) 81 | 82 | 83 | gen_octagonal = partial(gen_number, base=8) 84 | gen_hexadecimal = partial(gen_number, base=16) 85 | 86 | 87 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 88 | 89 | 90 | def __dir__(): 91 | return __all__ 92 | -------------------------------------------------------------------------------- /fauxfactory/factories/strings.py: -------------------------------------------------------------------------------- 1 | """Collection of string generating functions.""" 2 | 3 | import random 4 | import string 5 | 6 | from fauxfactory.constants import HTML_TAGS, LOREM_IPSUM_TEXT 7 | from fauxfactory.helpers import ( 8 | check_len, 9 | check_validation, 10 | is_positive_int, 11 | unicode_letters_generator, 12 | ) 13 | 14 | 15 | def gen_string(str_type, length=None, validator=None, default=None, tries=10): 16 | """Call other string generation methods. 17 | 18 | :param str str_type: The type of string which should be generated. 19 | :param int length: The length of the generated string. Must be 1 or 20 | greater. 21 | :param validator: Function or regex (str). 22 | If a function it must receive one parameter and return True if value 23 | can be used and False of another value need to be generated. 24 | If str it will be used as regex to validate the generated value. 25 | Default is None which will not validate the value. 26 | :param tries: number of times validator must be called before returning 27 | `default`. Default is 10. 28 | :param default: If validator returns false a number of `tries` times, this 29 | value is returned instead. Must be defined if validator is not None 30 | :raises: ``ValueError`` if an invalid ``str_type`` is specified. 31 | :returns: A string. 32 | :rtype: str 33 | 34 | Valid values for ``str_type`` are as follows: 35 | 36 | * alpha 37 | * alphanumeric 38 | * cjk 39 | * cyrillic 40 | * html 41 | * latin1 42 | * numeric 43 | * utf8 44 | * punctuation 45 | 46 | """ 47 | str_types_functions = { 48 | "alpha": gen_alpha, 49 | "alphanumeric": gen_alphanumeric, 50 | "cjk": gen_cjk, 51 | "cyrillic": gen_cyrillic, 52 | "html": gen_html, 53 | "latin1": gen_latin1, 54 | "numeric": gen_numeric_string, 55 | "utf8": gen_utf8, 56 | "punctuation": gen_special, 57 | } 58 | str_type_lower = str_type.lower() # do not modify user data 59 | if str_type_lower not in str_types_functions: 60 | raise ValueError( 61 | f"{str_type_lower} is not a supported string type. " 62 | "Valid string types are {0}.".format(",".join(str_types_functions.keys())) 63 | ) 64 | method = str_types_functions[str_type_lower] 65 | if length is None: 66 | return method(validator=validator, default=default, tries=tries) 67 | return method(length, validator=validator, default=default, tries=tries) 68 | 69 | 70 | @check_len 71 | @check_validation 72 | def gen_alpha(length=10, start=None, separator=""): 73 | """Return a random string made up of alpha characters. 74 | 75 | :param int length: Length for random data. 76 | :param str start: Random data start with. 77 | :param str separator: Separator character for start and random data. 78 | :returns: A random string made up of alpha characters. 79 | :rtype: str 80 | 81 | """ 82 | output_string = "".join(random.choices(string.ascii_letters, k=length)) 83 | 84 | if start: 85 | output_string = f"{start}{separator}{output_string}"[:length] 86 | return output_string 87 | 88 | 89 | @check_len 90 | @check_validation 91 | def gen_alphanumeric(length=10, start=None, separator=""): 92 | """Return a random string made up of alpha and numeric characters. 93 | 94 | :param int length: Length for random data. 95 | :param str start: Random data start with. 96 | :param str separator: Separator character for start and random data. 97 | :returns: A random string made up of alpha and numeric characters. 98 | :rtype: str 99 | 100 | """ 101 | output_string = "".join( 102 | random.choices(string.ascii_letters + string.digits, k=length) 103 | ) 104 | 105 | if start: 106 | output_string = f"{start}{separator}{output_string}"[:length] 107 | return output_string 108 | 109 | 110 | @check_len 111 | @check_validation 112 | def gen_cjk(length=10, start=None, separator=""): 113 | """Return a random string made up of CJK characters. 114 | 115 | (Source: Wikipedia - CJK Unified Ideographs) 116 | 117 | :param int length: Length for random data. 118 | :param str start: Random data start with. 119 | :param str separator: Separator character for start and random data. 120 | :returns: A random string made up of CJK characters. 121 | :rtype: str 122 | 123 | """ 124 | # These should represent the ranges for valid CJK characters 125 | unicode_block = ( 126 | # CJK Unified Ideographs 127 | (0x4E00, 0x9FFF), 128 | # CJK Unified Ideographs Extension A 129 | (0x3400, 0x4DBF), 130 | # CJK Unified Ideographs Extension B 131 | (0x20000, 0x2A6DF), 132 | # CJK Unified Ideographs Extension C 133 | (0x2A700, 0x2B73F), 134 | # CJK Unified Ideographs Extension D 135 | (0x2B740, 0x2B81F), 136 | ) 137 | 138 | output_array = [] 139 | 140 | for code_block in unicode_block: 141 | for i in range(*code_block): 142 | output_array.append(i) 143 | 144 | output_string = "".join(map(chr, random.choices(output_array, k=length))) 145 | 146 | if start: 147 | output_string = f"{start}{separator}{output_string}"[0:length] 148 | return output_string 149 | 150 | 151 | @check_len 152 | @check_validation 153 | def gen_cyrillic(length=10, start=None, separator=""): 154 | """Return a random string made up of Cyrillic characters. 155 | 156 | :param int length: Length for random data. 157 | :param str start: Random data start with. 158 | :param str separator: Separator character for start and random data. 159 | :returns: A random string made up of Cyrillic characters. 160 | :rtype: str 161 | 162 | """ 163 | # Generate codepoints, then convert the codepoints to a string. The 164 | # valid range of Cyrillic codepoints is 0x0400 - 0x04FF, inclusive. 165 | codepoints = [random.randint(0x0400, 0x04FF) for _ in range(length)] 166 | output_string = "".join(chr(codepoint) for codepoint in codepoints) 167 | 168 | if start: 169 | output_string = f"{start}{separator}{output_string}"[0:length] 170 | return output_string 171 | 172 | 173 | @check_len 174 | @check_validation 175 | def gen_html(length=10, include_tags=True): 176 | """Return a random string made up of html characters. 177 | 178 | :param int length: Length for random data. 179 | :returns: A random string made up of html characters. 180 | :rtype: str 181 | 182 | """ 183 | html_tag = random.choice(HTML_TAGS) 184 | 185 | if not include_tags: 186 | if length < 8: 187 | raise ValueError("Cannot generate html with less than 7 chars") 188 | maybe_len = length - len(f"<{html_tag}>") 189 | if maybe_len <= 0: 190 | length -= 7 191 | html_tag = "a" 192 | else: 193 | length = maybe_len 194 | output_string = f"<{html_tag}>{gen_string('alpha', length)}" 195 | else: 196 | output_string = f"<{html_tag}>{gen_string('alpha', length)}" 197 | 198 | return output_string 199 | 200 | 201 | def gen_iplum(words=None, paragraphs=None): 202 | """Return a lorem ipsum string. 203 | 204 | If no arguments are passed, then return the entire default lorem ipsum 205 | string. 206 | 207 | :param int words: The number of words to return. 208 | :param int paragraphs: The number of paragraphs to return. 209 | :raises: ``ValueError`` if ``words`` is not a valid positive integer. 210 | :returns: A ``lorem ipsum`` string containing either the number of 211 | ``words`` or ``paragraphs``, extending and wrapping around the text 212 | as needed to make sure that it has the specified length. 213 | :rtype: str 214 | 215 | """ 216 | # Check parameters 217 | if words is None or words == 0: 218 | words = len(LOREM_IPSUM_TEXT.split()) 219 | if paragraphs is None: 220 | paragraphs = 1 221 | 222 | if not isinstance(words, int) or words < 0: 223 | raise ValueError("Cannot generate a string with negative number of words.") 224 | is_positive_int(paragraphs) 225 | 226 | # Original Lorem Ipsum string 227 | all_words = LOREM_IPSUM_TEXT.split() 228 | # How many words do we need? 229 | total_words_needed = words * paragraphs 230 | 231 | quotient = int(total_words_needed / len(all_words)) 232 | modulus = total_words_needed % len(all_words) 233 | 234 | # Pool of words to use 235 | all_words = all_words * (quotient + modulus) 236 | 237 | result = "" 238 | start_pos = 0 239 | for _ in range(0, paragraphs): 240 | sentence = " ".join(all_words[start_pos : start_pos + words]) 241 | 242 | # Remove comma from the end, if it exists 243 | if sentence.endswith(","): 244 | sentence = sentence.rstrip(",") 245 | # Remove period from the end, if it exists 246 | if sentence.endswith("."): 247 | sentence = sentence.rstrip(".") 248 | 249 | # Each sentence should be properly capitalized 250 | cap_sentence = [frag.capitalize() + "." for frag in sentence.split(". ")] 251 | 252 | # Add newline at the end 253 | result += " ".join(cap_sentence) + "\n" 254 | 255 | # Increment positional counter 256 | start_pos += words 257 | return result.rstrip() 258 | 259 | 260 | @check_len 261 | @check_validation 262 | def gen_latin1(length=10, start=None, separator=""): 263 | """Return a random string made up of UTF-8 characters. 264 | 265 | (Font: Wikipedia - Latin-1 Supplement Unicode Block) 266 | 267 | :param int length: Length for random data. 268 | :param str start: Random data start with. 269 | :param str separator: Separator character for start and random data. 270 | :returns: A random string made up of ``Latin1`` characters. 271 | :rtype: str 272 | 273 | """ 274 | range0 = range1 = range2 = [] 275 | range0 = ["00C0", "00D6"] 276 | range1 = ["00D8", "00F6"] 277 | range2 = ["00F8", "00FF"] 278 | output_array = [] 279 | 280 | for i in range(int(range0[0], 16), int(range0[1], 16)): 281 | output_array.append(i) 282 | for i in range(int(range1[0], 16), int(range1[1], 16)): 283 | output_array.append(i) 284 | for i in range(int(range2[0], 16), int(range2[1], 16)): 285 | output_array.append(i) 286 | 287 | output_string = "".join(map(chr, random.choices(output_array, k=length))) 288 | 289 | if start: 290 | output_string = f"{start}{separator}{output_string}"[0:length] 291 | return output_string 292 | 293 | 294 | @check_len 295 | @check_validation 296 | def gen_numeric_string(length=10, start=None, separator=""): 297 | """Return a random string made up of numbers. 298 | 299 | :param int length: Length for random data. 300 | :param str start: Random data start with. 301 | :param str separator: Separator character for start and random data. 302 | :returns: A random string made up of numbers. 303 | :rtype: str 304 | 305 | """ 306 | output_string = "".join(random.choices(string.digits, k=length)) 307 | 308 | if start: 309 | output_string = f"{start}{separator}{output_string}"[0:length] 310 | return output_string 311 | 312 | 313 | @check_len 314 | @check_validation 315 | def gen_utf8(length=10, smp=True, start=None, separator=""): 316 | """Return a random string made up of UTF-8 letters characters. 317 | 318 | Follows `RFC 3629`_. 319 | 320 | :param int length: Length for random data. 321 | :param str start: Random data start with. 322 | :param str separator: Separator character for start and random data. 323 | :param bool smp: Include Supplementary Multilingual Plane (SMP) 324 | characters 325 | :returns: A random string made up of ``UTF-8`` letters characters. 326 | :rtype: str 327 | 328 | .. _`RFC 3629`: http://www.rfc-editor.org/rfc/rfc3629.txt 329 | 330 | """ 331 | unicode_letters = list(unicode_letters_generator(smp)) 332 | output_string = "".join(random.choices(unicode_letters, k=length)) 333 | 334 | if start: 335 | output_string = f"{start}{separator}{output_string}"[0:length] 336 | return output_string 337 | 338 | 339 | @check_len 340 | @check_validation 341 | def gen_special(length=10, start=None, separator=""): 342 | """Return a random special characters string. 343 | 344 | :param int length: Length for random data. 345 | :param str start: Random data start with. 346 | :param str separator: Separator character for start and random data. 347 | :returns: A random string made up of special characters. 348 | :rtype: str 349 | """ 350 | output_string = "".join(random.choices(string.punctuation, k=length)) 351 | 352 | if start: 353 | output_string = f"{start}{separator}{output_string}"[0:length] 354 | return output_string 355 | 356 | 357 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 358 | 359 | 360 | def __dir__(): 361 | return __all__ 362 | -------------------------------------------------------------------------------- /fauxfactory/factories/systems.py: -------------------------------------------------------------------------------- 1 | """Collection of computer systems generating functions.""" 2 | 3 | from copy import deepcopy 4 | 5 | from fauxfactory.constants import FACTS_JSON 6 | from fauxfactory.helpers import is_positive_int 7 | 8 | from .choices import gen_choice, gen_uuid 9 | from .internet import gen_domain, gen_ipaddr, gen_mac, gen_netmask 10 | from .numbers import gen_integer 11 | from .strings import gen_alpha, gen_alphanumeric 12 | 13 | 14 | def add_memory_info(count=None): 15 | """Generate fake memory facts. 16 | 17 | :param int count: The total amount of RAM for a system. 18 | :returns: A dictionary representing memory facts. 19 | :rtype: dict 20 | 21 | """ 22 | if count is None: 23 | count = gen_choice(range(4, 128, 2)) 24 | else: 25 | is_positive_int(count) 26 | 27 | free_ram = gen_integer(min_value=4, max_value=count) 28 | 29 | return { 30 | "dmi::memory::size": f"{free_ram * 1024}", 31 | "memoryfree": f"{free_ram} GB", 32 | "memoryfree_mb": f"{free_ram * 1024}", 33 | "memorysize": f"{count} GB", 34 | "memorysize_mb": f"{count * 1024}", 35 | "swapfree": f"{free_ram} GB", 36 | "swapfree_mb": f"{free_ram * 1024}", 37 | "swapsize": f"{count} GB", 38 | "swapsize_mb": f"{count * 1024}", 39 | } 40 | 41 | 42 | def add_network_devices(): 43 | """Generate fake network device facts. 44 | 45 | :returns: A dictionary representing a Host's network devices. 46 | :rtype: dict 47 | 48 | """ 49 | return { 50 | "dhcp_servers": { 51 | "system": gen_ipaddr(), 52 | "enp11s0": gen_ipaddr(), 53 | }, 54 | "interfaces": "enp11s0", 55 | "ipaddress": gen_ipaddr(), 56 | "ipaddress6": gen_ipaddr(ipv6=True), 57 | "ipaddress6_enp11s0": gen_ipaddr(ipv6=True), 58 | "macaddress": gen_mac(), 59 | "macaddress_enp11s0": gen_mac(), 60 | "mtu_enp11s0": "1500", 61 | "netmask": gen_netmask(), 62 | "netmask_enp11s0": gen_netmask(), 63 | "netmask_lo": gen_netmask(), 64 | "network_enp11s0": gen_ipaddr(), 65 | "network_lo": "127.0.0.0", 66 | } 67 | 68 | 69 | def add_operating_system(name=None, family=None, major=None, minor=None): 70 | """Generate fake operating system facts. 71 | 72 | :param str name: The name for an operating system. 73 | :param str family: The operating system family. 74 | :param int major: The major release of the operating system. 75 | :param int minor: The minor release of the operating system. 76 | :returns: A dictionary representing an Operating System. 77 | :rtype: dict 78 | 79 | """ 80 | if name is None: 81 | name = gen_alpha() 82 | if family is None: 83 | family = gen_alpha() 84 | if major is None: 85 | major = gen_integer(min_value=0, max_value=9) 86 | if minor is None: 87 | minor = gen_integer(min_value=0, max_value=9) 88 | return { 89 | "os": { 90 | "name": name, 91 | "family": family, 92 | "release": {"major": major, "minor": minor, "full": f"{major}.{minor}"}, 93 | }, 94 | "operatingsystem": name, 95 | "operatingsystemmajrelease": major, 96 | "operatingsystemrelease": f"{major}.{minor}", 97 | "osfamily": family, 98 | "distribution::id": gen_alpha(), 99 | "distribution::name": name, 100 | "distribution::version": f"{major}.{minor}", 101 | } 102 | 103 | 104 | def add_partitions(extra_partitions=None): 105 | """Generate fake partitions facts.""" 106 | partitions = { 107 | "partitions": { 108 | "sda1": { 109 | "uuid": gen_uuid(), 110 | "size": "1024000", 111 | "mount": "/boot", 112 | "filesystem": "xfs", 113 | }, 114 | } 115 | } 116 | 117 | if extra_partitions is not None: 118 | is_positive_int(extra_partitions) 119 | for idx in range(extra_partitions): 120 | device_id = idx + 1 121 | partitions["partitions"].update( 122 | { 123 | f"sdb{device_id}": { 124 | "size": "975747072", 125 | "filesystem": "LVM2_member", 126 | } 127 | } 128 | ) 129 | return partitions 130 | 131 | 132 | def add_processor_info(count=None): 133 | """Generate fake processor facts. 134 | 135 | :param int count: Number of processors for a system. 136 | :returns: A dictionary containing fake processor facts. 137 | :rtype: dict 138 | 139 | """ 140 | if count is None: 141 | count = gen_choice(range(2, 16, 2)) 142 | else: 143 | is_positive_int(count) 144 | 145 | processors = { 146 | "physicalprocessorcount": count, 147 | "processorcount": count, 148 | "cpu::topology_source": "kernel /sys cpu sibling lists", 149 | "cpu::cpu(s)": count, 150 | "lscpu::cpu(s)": count, 151 | "processors": { 152 | "models": [], 153 | "count": count, 154 | "physicalcount": count, 155 | }, 156 | } 157 | 158 | # Add processors info based on total processors 159 | for idx in range(count): 160 | processors[f"processor{idx}"] = "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz" 161 | processors["processors"]["models"].append( 162 | "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz" 163 | ) 164 | return processors 165 | 166 | 167 | def gen_system_facts(name=None): 168 | """Generate system facts. 169 | 170 | See https://docs.puppet.com/facter/3.6/core_facts.html for more 171 | information. 172 | 173 | :param str name: Name to be used as the system's hostname. 174 | :returns: A Dictionary representing a system's facts. 175 | :rtype: dict 176 | """ 177 | if name is None or name == "": 178 | fqdn = gen_domain() 179 | else: 180 | fqdn = gen_domain(*name.split(".")) 181 | 182 | kernel = ".".join([f"{gen_integer(min_value=0, max_value=9)}" for _ in range(3)]) 183 | 184 | host = deepcopy(FACTS_JSON) 185 | 186 | host["architecture"] = gen_choice(("i386", "x86_64")) 187 | host["domain"] = ".".join(fqdn.split(".")[1:]) 188 | host["fqdn"] = fqdn 189 | host["hostname"] = fqdn.split(".")[0] 190 | 191 | host["hardwareisa"] = host["architecture"] 192 | host["hardwaremodel"] = host["architecture"] 193 | host["kernelmajversion"] = ".".join(kernel.split(".")[:2]) 194 | host["kernelrelease"] = ( 195 | f"{kernel}-{gen_integer(min_value=0, max_value=999)}.{host['architecture']}" 196 | ) 197 | host["kernelversion"] = kernel 198 | 199 | host.update(add_memory_info()) 200 | 201 | host.update(add_network_devices()) 202 | 203 | host.update(add_operating_system()) 204 | 205 | host.update(add_partitions()) 206 | 207 | host.update(add_processor_info()) 208 | 209 | host["path"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/sbin" 210 | 211 | host["productname"] = f"{gen_alpha()}" 212 | host["dmi::system::product_name"] = host["productname"] 213 | host["dmi::baseboard::product_name"] = host["productname"] 214 | host["serialnumber"] = f"{gen_alphanumeric()}" 215 | host["timezone"] = "EDT" 216 | host["uniqueid"] = f"{gen_alphanumeric()}" 217 | host["uptime_days"] = gen_integer(min_value=1, max_value=1974) 218 | host["uptime_hours"] = host["uptime_days"] * 24 219 | host["uptime_seconds"] = host["uptime_hours"] * 3600 220 | host["uptime"] = f"{host['uptime_days']} days" 221 | host["system_uptime"] = { 222 | "seconds": host["uptime_seconds"], 223 | "hours": host["uptime_hours"], 224 | "days": host["uptime_days"], 225 | "uptime": host["uptime"], 226 | } 227 | host["uuid"] = gen_uuid() 228 | host["dmi::system::uuid"] = host["uuid"] 229 | 230 | return host 231 | 232 | 233 | __all__ = tuple(name for name in locals() if name.startswith("gen_")) 234 | 235 | 236 | def __dir__(): 237 | return __all__ 238 | -------------------------------------------------------------------------------- /fauxfactory/facts.json: -------------------------------------------------------------------------------- 1 | { 2 | "architecture": "x86_64", 3 | "augeasversion": "1.4.0", 4 | "bios_release_date": "06/27/2012", 5 | "bios_vendor": "IBM Corp.", 6 | "bios_version": "-[JQE150AUS-1.02]-", 7 | "blockdevice_sda_model": "ST500NM0011", 8 | "blockdevice_sda_size": "500107862016", 9 | "blockdevice_sda_vendor": "ATA", 10 | "blockdevice_sr0_model": "DEVICE 81Y3657", 11 | "blockdevice_sr0_size": "1073741312", 12 | "blockdevice_sr0_vendor": "IBM SATA", 13 | "blockdevices": "sda,sr0", 14 | "boardmanufacturer": "IBM", 15 | "boardproductname": "00D3729", 16 | "boardserialnumber": "29L0B1", 17 | "dhcp_servers": {"system": "10.16.36.29", "enp11s0": "10.16.36.29"}, 18 | "domain": "idmqe.lab.eng.bos.redhat.com", 19 | "facterversion": "2.4.6", 20 | "filesystems": "iso9660,xfs", 21 | "fqdn": "cloud-qe-10.idmqe.lab.eng.bos.redhat.com", 22 | "gid": "root", 23 | "hardwareisa": "x86_64", 24 | "hardwaremodel": "x86_64", 25 | "hostname": "cloud-qe-10", 26 | "id": "root", 27 | "interfaces": "docker0,enp0s29u1u1u5,enp11s0,lo,virbr1,virbr1_nic,vnet0,vnet2,vnet3", 28 | "ipaddress": "172.17.0.1", 29 | "ipaddress6": "2620:52:0:1322:3640:b5ff:fe8a:46fc", 30 | "ipaddress6_enp11s0": "2620:52:0:1322:3640:b5ff:fe8a:46fc", 31 | "ipaddress_enp11s0": "10.19.34.41", 32 | "ipaddress_lo": "127.0.0.1", 33 | "is_pe": "false", 34 | "is_virtual": "false", 35 | "kernel": "Linux", 36 | "kernelmajversion": "3.10", 37 | "kernelrelease": "3.10.0-327.el7.x86_64", 38 | "kernelversion": "3.10.0", 39 | "macaddress": "02:42:f3:ac:19:41", 40 | "macaddress_enp11s0": "34:40:b5:8a:46:fc", 41 | "manufacturer": "IBM", 42 | "memoryfree": "6.62 GB", 43 | "memoryfree_mb": "6779.48", 44 | "memorysize": "15.50 GB", 45 | "memorysize_mb": "15876.03", 46 | "mtu_enp11s0": "1500", 47 | "mtu_lo": "65536", 48 | "netmask": "255.255.0.0", 49 | "netmask_enp11s0": "255.255.254.0", 50 | "netmask_lo": "255.0.0.0", 51 | "network_enp11s0": "10.19.34.0", 52 | "network_lo": "127.0.0.0", 53 | "operatingsystem": "RedHat", 54 | "operatingsystemmajrelease": "7", 55 | "operatingsystemrelease": "7.3", 56 | "os": {"name": "RedHat", "family": "RedHat", "release": {"major": "7", "minor": "3", "full": "7.3"}}, 57 | "osfamily": "RedHat", 58 | "partitions": {"sda1": {"uuid": "06a5f231-72a2-4f7e-bcfc-663c7c65b2d8", "size": "1024000", "mount": "/boot", "filesystem": "xfs"}, "sda2": {"size": "975747072", "filesystem": "LVM2_member"}}, 59 | "path": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/sbin", 60 | "physicalprocessorcount": "1", 61 | "processor0": "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", 62 | "processor1": "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", 63 | "processor2": "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", 64 | "processor3": "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", 65 | "processorcount": "4", 66 | "processors": {"models": ["Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz", "Intel(R) Xeon(R) CPU E31220 @ 3.10GHz"], "count": 4, "physicalcount": 1}, 67 | "productname": "IBM System X3250 M4 -[2583AC1]-", 68 | "ps": "ps -ef", 69 | "puppet_vardir": "/var/lib/puppet", 70 | "puppetversion": "3.8.6", 71 | "root_home": "/root", 72 | "rubyplatform": "x86_64-linux", 73 | "rubysitedir": "/usr/local/share/ruby/site_ruby/", 74 | "rubyversion": "2.0.0", 75 | "selinux": "true", 76 | "selinux_config_mode": "enforcing", 77 | "selinux_config_policy": "targeted", 78 | "selinux_current_mode": "enforcing", 79 | "selinux_enforced": "true", 80 | "selinux_policyversion": "28", 81 | "serialnumber": "KQ3X4C1", 82 | "sshecdsakey": "AAAAE", 83 | "sshed25519key": "AAAAC", 84 | "sshfp_ecdsa": "SSHFP 3 1 74e732fc8e7bb5a2dbd7a12f640d8f294ef71fa7", 85 | "sshfp_ed25519": "SSHFP 4 1 0cd03c77d5930980f9cc5c9653cdb48eaefe4e08", 86 | "sshfp_rsa": "SSHFP 1 1 c34f4c13c6af5c3d7567d6a26415e15f9e5ca974", 87 | "sshrsakey": "AAAAB3", 88 | "swapfree": "7.70 GB", 89 | "swapfree_mb": "7886.85", 90 | "swapsize": "7.88 GB", 91 | "swapsize_mb": "8064.00", 92 | "system_uptime": {"seconds": 18658441, "hours": 5182, "days": 215, "uptime": "215 days"}, 93 | "timezone": "EDT", 94 | "uniqueid": "130a2922", 95 | "uptime": "215 days", 96 | "uptime_days": "215", 97 | "uptime_hours": "5182", 98 | "uptime_seconds": "18658441", 99 | "uuid": "D83DA472-C1E4-3BF2-BC8A-EEC4ACC29391", 100 | "virtual": "physical" 101 | } 102 | -------------------------------------------------------------------------------- /fauxfactory/helpers.py: -------------------------------------------------------------------------------- 1 | """Collection of helper methods and functions.""" 2 | 3 | import re 4 | import unicodedata 5 | from collections import namedtuple 6 | from functools import wraps 7 | 8 | from fauxfactory.constants import VALID_DIGITS 9 | 10 | UnicodePlane = namedtuple("UnicodePlane", ["min", "max"]) 11 | 12 | BMP = UnicodePlane(int("0x0000", 16), int("0xffff", 16)) 13 | SMP = UnicodePlane(int("0x10000", 16), int("0x1ffff", 16)) 14 | 15 | 16 | def base_repr(number, base): 17 | """Return the base representation of a decimal number. 18 | 19 | As shared here: https://stackoverflow.com/a/2267446 20 | 21 | Conversion steps: 22 | 23 | 1) Divide the number by the base 24 | 2) Get the integer quotient for the next iteration 25 | 3) Get the remainder for the hex digit 26 | 4) Repeat the steps until quotient is equal to zero 27 | 28 | :param number: (int) The decimal number to be converted. 29 | :param base: The base to convert. 30 | 31 | :return: The base representation of . 32 | """ 33 | if base <= 1: 34 | raise ValueError("Cannot represent number with base smaller than 2.") 35 | if number < 0: 36 | sign = -1 37 | elif number == 0: 38 | return VALID_DIGITS[0] 39 | else: 40 | sign = 1 41 | 42 | number *= sign 43 | digits = [] 44 | 45 | while number: 46 | digits.append(VALID_DIGITS[number % base]) 47 | number //= base 48 | 49 | if sign < 0: 50 | digits.append("-") 51 | 52 | digits.reverse() 53 | 54 | return "".join(digits) 55 | 56 | 57 | def check_len(fnc): 58 | """Validate generators requiring a `length` argument.""" 59 | 60 | @wraps(fnc) 61 | def wrapped(*args, **kwargs): 62 | """Make sure that we verify the `length` argument.""" 63 | if args and len(args) == 1: 64 | is_positive_int(args[0]) 65 | if "length" in kwargs: 66 | if kwargs.get("length") is not None: 67 | is_positive_int(kwargs.get("length")) 68 | 69 | result = fnc(*args, **kwargs) 70 | 71 | return result 72 | 73 | return wrapped 74 | 75 | 76 | def check_validation(fcn): 77 | """Decorate functions requiring validation. 78 | 79 | Simple decorator to validate values generated by function `fnc` 80 | according to parameters `validator`, `default` and `tries`. 81 | 82 | :param fcn: function to be enhanced 83 | :return: decorated function 84 | """ 85 | 86 | @wraps(fcn) 87 | def validate(*args, **kwargs): 88 | """Perform the validation on decorated function.""" 89 | validator = kwargs.get("validator") 90 | default = kwargs.get("default") 91 | tries = kwargs.get("tries", 10) 92 | if validator and default is None: 93 | raise ValueError( 94 | 'If "validator" param is defined, "default" parameter must not be None' 95 | ) 96 | if validator is None: 97 | 98 | def validator_fcn(_): 99 | """No validation passed.""" 100 | return True 101 | 102 | else: 103 | validator_fcn = validator 104 | 105 | if not callable(validator_fcn): 106 | 107 | def regex_validator(value): 108 | """Perform RegEx validation.""" 109 | return re.match(validator, value) 110 | 111 | validator_fcn = regex_validator 112 | 113 | # Removing params related to validation but not fcn 114 | for key in ("validator", "default", "tries"): 115 | if key in kwargs: 116 | kwargs.pop(key) 117 | 118 | for _ in range(tries): 119 | value = fcn(*args, **kwargs) 120 | if validator_fcn(value): 121 | return value 122 | 123 | return default 124 | 125 | return validate 126 | 127 | 128 | def is_positive_int(length): 129 | """Check that `length` argument is an integer greater than zero. 130 | 131 | :param int length: The desired length of the string 132 | :raises: `ValueError` if `length` is not an `int` or is less than 1. 133 | 134 | """ 135 | if not isinstance(length, int): 136 | raise ValueError(f"{length} is not numeric.") 137 | if length <= 0: 138 | raise ValueError(f"{length} is an invalid length.") 139 | 140 | 141 | def unicode_letters_generator(smp=True): 142 | """Generate unicode characters in the letters category. 143 | 144 | :param bool smp: Include Supplementary Multilingual Plane (SMP) 145 | characters 146 | :return: a generator which will generates all unicode letters available 147 | 148 | """ 149 | for i in range(BMP.min, SMP.max if smp else BMP.max): 150 | char = chr(i) 151 | if unicodedata.category(char).startswith("L"): 152 | yield char 153 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "fauxfactory" 3 | version = "3.1.2" 4 | license = { text = "Apache-2.0" } 5 | description = "Generates random data for your tests." 6 | authors = [ 7 | { name = "Og B. Maciel", email = "omaciel@ogmaciel.com" } 8 | ] 9 | readme = "README.rst" 10 | requires-python = ">= 3.9" 11 | 12 | keywords = [ 13 | "automation", 14 | "data", 15 | "python", 16 | "testing", 17 | ] 18 | classifiers = [ 19 | "Development Status :: 5 - Production/Stable", 20 | "Intended Audience :: Developers", 21 | "Natural Language :: English", 22 | "License :: OSI Approved :: Apache Software License", 23 | "Operating System :: OS Independent", 24 | "Programming Language :: Python", 25 | "Programming Language :: Python :: 3.9", 26 | "Programming Language :: Python :: 3.10", 27 | "Programming Language :: Python :: 3.11", 28 | "Programming Language :: Python :: 3.12", 29 | "Topic :: Software Development :: Testing", 30 | ] 31 | 32 | [project.optional-dependencies] 33 | dev = [ 34 | "pytest>=8.1.1", 35 | "codecov>=2.1.13", 36 | "pytest-cov>=5.0.0", 37 | "wheel>=0.43.0", 38 | "twine>=5.0.0", 39 | "ruff>=0.3.4", 40 | ] 41 | docs = [ 42 | "sphinx", 43 | "sphinx-rtd-theme" 44 | ] 45 | 46 | [project.urls] 47 | Changelog = "https://github.com/omaciel/fauxfactory/blob/master/HISTORY.rst" 48 | Homepage = "https://github.com/omaciel/fauxfactory" 49 | Repository = "https://github.com/omaciel/fauxfactory" 50 | 51 | 52 | [build-system] 53 | requires = ["hatchling"] 54 | build-backend = "hatchling.build" 55 | 56 | [tool.hatch.build] 57 | exclude = ["tests/", "docs/"] # Exclude test and doc directories 58 | include = ["fauxfactory/**/*", "README.rst", "LICENSE"] # Ensure only necessary files are packaged 59 | directory = "dist" 60 | 61 | [tool.hatch.build.targets.sdist] 62 | include = ["fauxfactory/**/*", "README.rst", "LICENSE"] 63 | 64 | [tool.hatch.metadata] 65 | allow-direct-references = true 66 | 67 | [tool.hatch.build.targets.wheel] 68 | packages = ["fauxfactory"] 69 | macos-max-compat = true 70 | 71 | [tool.ruff] 72 | include = ["*.py"] 73 | target-version = "py312" 74 | 75 | [tool.ruff.lint] 76 | select = ["E", "F", "B", "I"] 77 | ignore = ["B018", "E203"] 78 | fixable = ["ALL"] 79 | unfixable = [] 80 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 81 | -------------------------------------------------------------------------------- /requirements-dev.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | alabaster==0.7.13 12 | # via sphinx 13 | babel==2.14.0 14 | # via sphinx 15 | certifi==2024.2.2 16 | # via requests 17 | charset-normalizer==3.3.2 18 | # via requests 19 | codecov==2.1.13 20 | coverage==7.4.4 21 | # via codecov 22 | # via pytest-cov 23 | docutils==0.20.1 24 | # via readme-renderer 25 | # via sphinx 26 | idna==3.6 27 | # via requests 28 | imagesize==1.4.1 29 | # via sphinx 30 | importlib-metadata==7.1.0 31 | # via twine 32 | iniconfig==2.0.0 33 | # via pytest 34 | jaraco-classes==3.3.1 35 | # via keyring 36 | jaraco-context==4.3.0 37 | # via keyring 38 | jaraco-functools==4.0.0 39 | # via keyring 40 | jinja2==3.1.3 41 | # via sphinx 42 | keyring==25.0.0 43 | # via twine 44 | markdown-it-py==3.0.0 45 | # via rich 46 | markupsafe==2.1.5 47 | # via jinja2 48 | mdurl==0.1.2 49 | # via markdown-it-py 50 | more-itertools==10.2.0 51 | # via jaraco-classes 52 | # via jaraco-functools 53 | nh3==0.2.17 54 | # via readme-renderer 55 | packaging==24.0 56 | # via pytest 57 | # via sphinx 58 | pkginfo==1.10.0 59 | # via twine 60 | pluggy==1.4.0 61 | # via pytest 62 | pygments==2.17.2 63 | # via readme-renderer 64 | # via rich 65 | # via sphinx 66 | pytest==8.1.1 67 | # via pytest-cov 68 | pytest-cov==5.0.0 69 | readme-renderer==43.0 70 | # via twine 71 | requests==2.31.0 72 | # via codecov 73 | # via requests-toolbelt 74 | # via sphinx 75 | # via twine 76 | requests-toolbelt==1.0.0 77 | # via twine 78 | rfc3986==2.0.0 79 | # via twine 80 | rich==13.7.1 81 | # via twine 82 | ruff==0.3.4 83 | snowballstemmer==2.2.0 84 | # via sphinx 85 | sphinx==7.2.6 86 | # via fauxfactory 87 | sphinxcontrib-applehelp==1.0.4 88 | # via sphinx 89 | sphinxcontrib-devhelp==1.0.2 90 | # via sphinx 91 | sphinxcontrib-htmlhelp==2.0.1 92 | # via sphinx 93 | sphinxcontrib-jsmath==1.0.1 94 | # via sphinx 95 | sphinxcontrib-qthelp==1.0.3 96 | # via sphinx 97 | sphinxcontrib-serializinghtml==1.1.10 98 | # via sphinx 99 | twine==5.0.0 100 | urllib3==2.2.1 101 | # via requests 102 | # via twine 103 | wheel==0.43.0 104 | zipp==3.18.1 105 | # via importlib-metadata 106 | -------------------------------------------------------------------------------- /requirements.lock: -------------------------------------------------------------------------------- 1 | # generated by rye 2 | # use `rye lock` or `rye sync` to update this lockfile 3 | # 4 | # last locked with the following flags: 5 | # pre: false 6 | # features: [] 7 | # all-features: false 8 | # with-sources: false 9 | 10 | -e file:. 11 | alabaster==0.7.16 12 | # via sphinx 13 | babel==2.14.0 14 | # via sphinx 15 | certifi==2024.2.2 16 | # via requests 17 | charset-normalizer==3.3.2 18 | # via requests 19 | docutils==0.20.1 20 | # via sphinx 21 | idna==3.6 22 | # via requests 23 | imagesize==1.4.1 24 | # via sphinx 25 | jinja2==3.1.3 26 | # via sphinx 27 | markupsafe==2.1.5 28 | # via jinja2 29 | packaging==24.0 30 | # via sphinx 31 | pygments==2.17.2 32 | # via sphinx 33 | requests==2.31.0 34 | # via sphinx 35 | snowballstemmer==2.2.0 36 | # via sphinx 37 | sphinx==7.2.6 38 | # via fauxfactory 39 | sphinxcontrib-applehelp==1.0.8 40 | # via sphinx 41 | sphinxcontrib-devhelp==1.0.6 42 | # via sphinx 43 | sphinxcontrib-htmlhelp==2.0.5 44 | # via sphinx 45 | sphinxcontrib-jsmath==1.0.1 46 | # via sphinx 47 | sphinxcontrib-qthelp==1.0.7 48 | # via sphinx 49 | sphinxcontrib-serializinghtml==1.1.10 50 | # via sphinx 51 | urllib3==2.2.1 52 | # via requests 53 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unittests for FauxFactory.""" 2 | -------------------------------------------------------------------------------- /tests/test_booleans.py: -------------------------------------------------------------------------------- 1 | """Tests for all boolean generators.""" 2 | 3 | import pytest 4 | 5 | from fauxfactory import gen_boolean 6 | 7 | 8 | @pytest.mark.parametrize("item", range(10)) 9 | def test_gen_boolean(item): 10 | """Create a random boolean value.""" 11 | assert isinstance(gen_boolean(), bool) 12 | -------------------------------------------------------------------------------- /tests/test_check_validation.py: -------------------------------------------------------------------------------- 1 | """Tests related to generator validation methods.""" 2 | 3 | from unittest import mock 4 | 5 | import pytest 6 | 7 | from fauxfactory.helpers import check_validation 8 | 9 | # pylint: disable=invalid-name 10 | # pylint: disable=E1123 11 | 12 | 13 | @check_validation 14 | def decorated_f(): 15 | """Test validation method with this simple decorated function.""" 16 | return "not a number" 17 | 18 | 19 | def test_no_validator_defined(): 20 | """Check result value of decorated function is returned.""" 21 | assert decorated_f() == "not a number" 22 | 23 | 24 | def test_validator_defined_with_no_default(): 25 | """Check defining validator but not default raises an error.""" 26 | with pytest.raises(ValueError): 27 | decorated_f(validator=lambda _: True) 28 | 29 | 30 | def test_regex(): 31 | """Check regex validation when validator is a string.""" 32 | assert decorated_f(validator=r"\d.*", default="my default") == "my default" 33 | assert decorated_f(validator=r".*", default="my default") == "not a number" 34 | 35 | 36 | def test_callable(): 37 | """Check validation when validator is a callable.""" 38 | my_callable = mock.Mock(return_value=False) 39 | 40 | # Default of 10 unsuccessful tries 41 | assert decorated_f(validator=my_callable, default="my default") == "my default" 42 | my_callable.assert_called_with("not a number") 43 | assert my_callable.call_count == 10 44 | 45 | # 1 unsuccessful try 46 | my_callable.reset_mock() 47 | assert ( 48 | decorated_f(validator=my_callable, default="my default", tries=1) 49 | == "my default" 50 | ) 51 | my_callable.assert_called_once_with("not a number") 52 | 53 | # 1 successful try 54 | my_callable.reset_mock() 55 | my_callable.return_value = True 56 | assert ( 57 | decorated_f(validator=my_callable, default="my default", tries=10) 58 | == "not a number" 59 | ) 60 | my_callable.assert_called_once_with("not a number") 61 | -------------------------------------------------------------------------------- /tests/test_choices.py: -------------------------------------------------------------------------------- 1 | """Tests for all choice generators.""" 2 | 3 | import string 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_choice 8 | 9 | 10 | @pytest.mark.parametrize("item", range(10)) 11 | def test_gen_choice_1(item): 12 | """Select a random value from integer values.""" 13 | choices = range(5) 14 | 15 | result = gen_choice(choices) 16 | assert result in choices 17 | 18 | 19 | @pytest.mark.parametrize("item", range(10)) 20 | def test_gen_choice_2(item): 21 | """Select a random value from alphanumeric values.""" 22 | choices = string.ascii_letters + string.digits 23 | 24 | result = gen_choice(choices) 25 | assert result in choices 26 | 27 | 28 | @pytest.mark.parametrize("item", range(10)) 29 | def test_gen_choice_3(item): 30 | """Select a random value from short list.""" 31 | choices = [ 32 | 1, 33 | ] 34 | 35 | result = gen_choice(choices) 36 | assert result == choices[0] 37 | 38 | 39 | @pytest.mark.parametrize("item", range(10)) 40 | def test_gen_choice_4(item): 41 | """Select a random value from longer list.""" 42 | choices = [1, 2, 3, 9, 10, 11, 100, 101, 102] 43 | 44 | result = gen_choice(choices) 45 | assert result in choices 46 | 47 | 48 | @pytest.mark.parametrize("item", range(10)) 49 | def test_gen_choice_5(item): 50 | """Select a random value from short tuple.""" 51 | choices = (1,) 52 | 53 | result = gen_choice(choices) 54 | assert result == choices[0] 55 | 56 | 57 | @pytest.mark.parametrize("item", range(10)) 58 | def test_gen_choice_6(item): 59 | """Select a random value from longer tuple.""" 60 | choices = ( 61 | 1, 62 | 2, 63 | 3, 64 | 9, 65 | 10, 66 | 11, 67 | 100, 68 | 101, 69 | 102, 70 | ) 71 | 72 | result = gen_choice(choices) 73 | assert result in choices 74 | 75 | 76 | def test_gen_choice_7(): 77 | """Select a random value from empty list.""" 78 | choices = [] 79 | 80 | with pytest.raises(ValueError): 81 | gen_choice(choices) 82 | 83 | 84 | def test_gen_choice_8(): 85 | """Select a random value from empty tuple.""" 86 | choices = () 87 | 88 | with pytest.raises(ValueError): 89 | gen_choice(choices) 90 | 91 | 92 | def test_gen_choice_9(): 93 | """Select a random value from empty dictionary.""" 94 | choices = {} 95 | 96 | with pytest.raises(ValueError): 97 | gen_choice(choices) 98 | 99 | 100 | def test_gen_choice_10(): 101 | """Select a random value from single dictionary.""" 102 | choices = {"Name": "Bob", "Age": 39} 103 | 104 | with pytest.raises(ValueError): 105 | gen_choice(choices) 106 | 107 | 108 | @pytest.mark.parametrize("item", range(10)) 109 | def test_gen_choice_11(item): 110 | """Select a random value from dictionary list.""" 111 | choices = [ 112 | {"Name": "Bob", "Age": 39}, 113 | {"Name": "Alice", "Age": 23}, 114 | {"Name": "Pete", "Age": 79}, 115 | ] 116 | 117 | result = gen_choice(choices) 118 | assert result in choices 119 | 120 | 121 | @pytest.mark.parametrize("item", range(10)) 122 | def test_gen_choice_12(item): 123 | """Select a random value from words list.""" 124 | choices = ["green", "yellow", "blue", "white"] 125 | 126 | result = gen_choice(choices) 127 | assert result in choices 128 | 129 | 130 | def test_gen_choice_13(): 131 | """Cannot use None for Choice generator.""" 132 | choices = None 133 | 134 | with pytest.raises(ValueError): 135 | gen_choice(choices) 136 | -------------------------------------------------------------------------------- /tests/test_dates.py: -------------------------------------------------------------------------------- 1 | """Tests for date generator.""" 2 | 3 | import datetime 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_date 8 | from fauxfactory.constants import MAX_YEARS, MIN_YEARS 9 | 10 | 11 | def test_gen_date_1(): 12 | """Create a date with no arguments.""" 13 | result = gen_date() 14 | assert isinstance(result, datetime.date) 15 | 16 | 17 | @pytest.mark.parametrize("item", range(10)) 18 | def test_gen_date_2(item): 19 | """Create a date with only min_date.""" 20 | # Today is... 21 | today = datetime.date.today() 22 | # Five days ago 23 | min_date = today - datetime.timedelta(5) 24 | 25 | assert gen_date(min_date=min_date) >= min_date 26 | 27 | 28 | @pytest.mark.parametrize("item", range(10)) 29 | def test_gen_date_3(item): 30 | """Create a date with only max_date.""" 31 | # Today is... 32 | today = datetime.date.today() 33 | # Five days into the future 34 | max_date = today + datetime.timedelta(5) 35 | 36 | assert gen_date(max_date=max_date) <= max_date 37 | 38 | 39 | @pytest.mark.parametrize("item", range(10)) 40 | def test_gen_date_4(item): 41 | """Create a date with both arguments.""" 42 | # Today is... 43 | today = datetime.date.today() 44 | # Five days ago 45 | min_date = today - datetime.timedelta(5) 46 | # Five days into the future 47 | max_date = today + datetime.timedelta(5) 48 | 49 | result = gen_date(min_date=min_date, max_date=max_date) 50 | assert result >= min_date 51 | assert result <= max_date 52 | 53 | 54 | @pytest.mark.parametrize("item", range(20)) 55 | def test_gen_date_5(item): 56 | """Create a date with min_date == 'None'.""" 57 | # min_date for the platform 58 | min_date = datetime.date.today() - datetime.timedelta(365 * MIN_YEARS) 59 | # max_date = min_date + 1 year 60 | max_date = min_date + datetime.timedelta(365 * 1) 61 | 62 | result = gen_date(min_date=None, max_date=max_date) 63 | assert result.year <= max_date.year 64 | assert result.year >= min_date.year 65 | 66 | 67 | @pytest.mark.parametrize("item", range(20)) 68 | def test_gen_date_6(item): 69 | """Create a date with max_date == 'None'.""" 70 | # max_date for the platform 71 | max_date = datetime.date.today() + datetime.timedelta(365 * MAX_YEARS) 72 | # min_date = max_date - 1 year 73 | min_date = max_date - datetime.timedelta(365 * 1) 74 | 75 | result = gen_date(min_date=min_date, max_date=None) 76 | assert result.year <= max_date.year 77 | assert result.year >= min_date.year 78 | 79 | 80 | @pytest.mark.parametrize("item", range(20)) 81 | def test_gen_date_7(item): 82 | """Create a date with specific date ranges.""" 83 | # min_date for the platform 84 | min_date = datetime.date.today() - datetime.timedelta(365 * MIN_YEARS) 85 | # max_date for the platform 86 | max_date = datetime.date.today() + datetime.timedelta(365 * MAX_YEARS) 87 | 88 | result = gen_date(min_date=min_date, max_date=max_date) 89 | assert result.year <= max_date.year 90 | assert result.year >= min_date.year 91 | 92 | 93 | def test_gen_date_8(): 94 | """Create a date with non-Date arguments.""" 95 | with pytest.raises(ValueError): 96 | gen_date(min_date="", max_date="") 97 | 98 | 99 | def test_gen_date_9(): 100 | """Create a date with non-Date arguments.""" 101 | with pytest.raises(ValueError): 102 | gen_date(min_date="abc", max_date="def") 103 | 104 | 105 | def test_gen_date_10(): 106 | """Create a date with non-Date arguments.""" 107 | with pytest.raises(ValueError): 108 | gen_date(min_date=1, max_date=1) 109 | 110 | 111 | def test_gen_date_11(): 112 | """Create a date with non-Date arguments.""" 113 | with pytest.raises(ValueError): 114 | gen_date(min_date=(1,), max_date=(2, 3, 4)) 115 | 116 | 117 | def test_gen_date_12(): 118 | """Create a date with non-Date arguments.""" 119 | with pytest.raises(ValueError): 120 | gen_date(min_date=["a", "b"], max_date=["c", "d", "e"]) 121 | 122 | 123 | def test_gen_date_13(): 124 | """Create a date with min_date > max_date.""" 125 | # Today is... 126 | today = datetime.date.today() 127 | # Five days into the future 128 | min_date = today + datetime.timedelta(5) 129 | 130 | with pytest.raises(AssertionError): 131 | gen_date(min_date=min_date, max_date=today) 132 | 133 | 134 | def test_gen_date_14(): 135 | """max-date must be a Date type.""" 136 | with pytest.raises(ValueError): 137 | gen_date(min_date=datetime.date.today(), max_date="foo") 138 | -------------------------------------------------------------------------------- /tests/test_datetime.py: -------------------------------------------------------------------------------- 1 | """Tests for datetime generator.""" 2 | 3 | import datetime 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_datetime 8 | from fauxfactory.constants import MAX_YEARS, MIN_YEARS 9 | 10 | 11 | def test_gen_datetime_1(): 12 | """Create a datetime with no arguments.""" 13 | assert isinstance(gen_datetime(), datetime.datetime) 14 | 15 | 16 | def test_gen_datetime_2(): 17 | """Create a datetime with only min_date.""" 18 | # Today is... 19 | today = datetime.datetime.now() 20 | # Five minutes ago 21 | min_date = today - datetime.timedelta(seconds=5 * 60) 22 | 23 | for _ in range(10): 24 | assert gen_datetime(min_date=min_date) >= min_date 25 | 26 | 27 | def test_gen_datetime_3(): 28 | """Create a datetime with only max_date.""" 29 | # Today is... 30 | today = datetime.datetime.now() 31 | # Five minutes into the future 32 | max_date = today + datetime.timedelta(seconds=5 * 60) 33 | 34 | for _ in range(10): 35 | assert gen_datetime(max_date=max_date) <= max_date 36 | 37 | 38 | def test_gen_datetime_4(): 39 | """Create a datetime with a 5-days datetime range.""" 40 | # Today is... 41 | today = datetime.datetime.now() 42 | # Five minutes ago 43 | min_date = today - datetime.timedelta(seconds=5 * 60) 44 | # Five minutes into the future 45 | max_date = today + datetime.timedelta(seconds=5 * 60) 46 | 47 | for _ in range(10): 48 | result = gen_datetime(min_date=min_date, max_date=max_date) 49 | assert result >= min_date 50 | assert result <= max_date 51 | 52 | 53 | def test_gen_datetime_5(): 54 | """Create a datetime with min_date = None.""" 55 | # min_date for the platform 56 | min_date = datetime.datetime.now() - datetime.timedelta(365 * MIN_YEARS) 57 | # max_date = min_date + 1 year 58 | max_date = min_date + datetime.timedelta(365 * 1) 59 | 60 | for _ in range(20): 61 | result = gen_datetime(min_date=None, max_date=max_date) 62 | assert result.year <= max_date.year 63 | assert result.year >= min_date.year 64 | 65 | 66 | def test_gen_datetime_6(): 67 | """Create a datetime with max_date == None.""" 68 | # max_date for the platform 69 | max_date = datetime.datetime.now() + datetime.timedelta(365 * MAX_YEARS) 70 | # min_date = max_date - 1 year 71 | min_date = max_date - datetime.timedelta(365 * 1) 72 | 73 | for _ in range(20): 74 | result = gen_datetime(min_date=min_date, max_date=None) 75 | assert result.year <= max_date.year 76 | assert result.year >= min_date.year 77 | 78 | 79 | def test_gen_datetime_7(): 80 | """Create a datetime with specified datetime ranges.""" 81 | # min_date for the platform 82 | min_date = datetime.datetime.now() - datetime.timedelta(365 * MIN_YEARS) 83 | # max_date for the platform 84 | max_date = datetime.datetime.now() + datetime.timedelta(365 * MAX_YEARS) 85 | 86 | for _ in range(20): 87 | result = gen_datetime(min_date=min_date, max_date=max_date) 88 | assert result.year <= max_date.year 89 | assert result.year >= min_date.year 90 | 91 | 92 | def test_gen_datetime_8(): 93 | """Create a datetime with non-Date arguments.""" 94 | with pytest.raises(ValueError): 95 | gen_datetime(min_date="", max_date="") 96 | 97 | 98 | def test_gen_datetime_9(): 99 | """Create a datetime with non-Date arguments.""" 100 | with pytest.raises(ValueError): 101 | gen_datetime(min_date="abc", max_date="def") 102 | 103 | 104 | def test_gen_datetime_10(): 105 | """Create a datetime with non-Date arguments.""" 106 | with pytest.raises(ValueError): 107 | gen_datetime(min_date=1, max_date=1) 108 | 109 | 110 | def test_gen_datetime_11(): 111 | """Create a datetime with non-Date arguments.""" 112 | with pytest.raises(ValueError): 113 | gen_datetime(min_date=(1,), max_date=(2, 3, 4)) 114 | 115 | 116 | def test_gen_datetime_12(): 117 | """Create a datetime with non-Date arguments.""" 118 | with pytest.raises(ValueError): 119 | gen_datetime(min_date=["a", "b"], max_date=["c", "d", "e"]) 120 | 121 | 122 | def test_gen_datetime_13(): 123 | """Create a datetime with min_date > max_date.""" 124 | # Today is... 125 | today = datetime.datetime.now() 126 | # Five minutes into the future 127 | min_date = today + datetime.timedelta(seconds=5 * 60) 128 | 129 | with pytest.raises(AssertionError): 130 | gen_datetime(min_date=min_date, max_date=today) 131 | 132 | 133 | def test_gen_date_14(): 134 | """max-date must be a Datetime type.""" 135 | with pytest.raises(ValueError): 136 | gen_datetime(min_date=datetime.datetime.now(), max_date="foo") 137 | -------------------------------------------------------------------------------- /tests/test_dir.py: -------------------------------------------------------------------------------- 1 | """Check of the __dir__ of the fauxfactory modules/packages. 2 | 3 | These tests are heavily just artificial tests to get 100% coverage. 4 | """ 5 | 6 | import os 7 | import os.path 8 | from importlib import import_module 9 | 10 | import fauxfactory.factories 11 | 12 | 13 | def check_all_dired_names_are_getattrable(obj): 14 | """Use dir to list symbol names and check getattr won't raise exception.""" 15 | for symbol_name in dir(obj): 16 | getattr(obj, symbol_name) 17 | 18 | 19 | def test_fauxfactory_factories_dir(): 20 | """Check dir of fauxfactory factories.""" 21 | for d in os.listdir(os.path.dirname(fauxfactory.factories.__file__)): 22 | if d.endswith(".py") and not d.startswith("__"): 23 | name = f".{d[:-3]}" 24 | module = import_module(name, fauxfactory.factories.__package__) 25 | check_all_dired_names_are_getattrable(module) 26 | 27 | 28 | def test_fauxfactory_dir(): 29 | """Check dir of fauxfactory itself.""" 30 | check_all_dired_names_are_getattrable(fauxfactory) 31 | -------------------------------------------------------------------------------- /tests/test_domain.py: -------------------------------------------------------------------------------- 1 | """Tests for domain generator.""" 2 | 3 | from fauxfactory import gen_domain 4 | 5 | 6 | def test_generate_domain(): 7 | """Create valid domain names.""" 8 | domain = gen_domain() 9 | assert len(domain.split(".")) == 3 10 | 11 | 12 | def test_generate_domain_with_attributes(): 13 | """Create valid domain names.""" 14 | domain = gen_domain(name="faux", subdomain="example", tlds="biz") 15 | assert len(domain.split(".")) == 3 16 | assert domain.split(".")[0] == "faux" 17 | assert domain.split(".")[1] == "example" 18 | assert domain.split(".")[2] == "biz" 19 | 20 | 21 | def test_generate_domain_with_name(): 22 | """Create valid domain names.""" 23 | domain = gen_domain(name="faux") 24 | assert len(domain.split(".")) == 3 25 | assert domain.split(".")[0] == "faux" 26 | 27 | 28 | def test_generate_domain_with_subdomain(): 29 | """Create valid domain names.""" 30 | domain = gen_domain(subdomain="example") 31 | assert len(domain.split(".")) == 3 32 | assert domain.split(".")[1] == "example" 33 | 34 | 35 | def test_generate_domain_with_tlds(): 36 | """Create valid domain names.""" 37 | domain = gen_domain(tlds="biz") 38 | assert len(domain.split(".")) == 3 39 | assert domain.split(".")[2] == "biz" 40 | -------------------------------------------------------------------------------- /tests/test_emails.py: -------------------------------------------------------------------------------- 1 | """Tests for Email generator.""" 2 | 3 | import re 4 | 5 | from fauxfactory import gen_email 6 | 7 | REGEX = r"^[a-zA-Z][a-zA-Z-.]*[^.-]@\w*\.[a-zA-Z]{2,3}" 8 | 9 | 10 | def test_gen_email_1(): 11 | """Create a random email value.""" 12 | # Regex for email validation 13 | emailinator = re.compile(REGEX) 14 | for _ in range(100): 15 | assert emailinator.match(gen_email()) 16 | -------------------------------------------------------------------------------- /tests/test_getattr.py: -------------------------------------------------------------------------------- 1 | """Check of the __getattr__ of the fauxfactory package. 2 | 3 | These tests are heavily just artificial tests to get 100% coverage. 4 | """ 5 | 6 | import pytest 7 | 8 | import fauxfactory 9 | 10 | 11 | def test_fauxfactory_getattr(): 12 | """Check __getattr__ returns expected objects.""" 13 | assert fauxfactory.__getattr__("gen_integer") is fauxfactory.gen_integer 14 | 15 | 16 | def test_fauxfactory_getattr_raises_attributeerror(): 17 | """Check __getattr__ raises AttributeError.""" 18 | with pytest.raises(AttributeError): 19 | fauxfactory.nonexistentattribute 20 | -------------------------------------------------------------------------------- /tests/test_html.py: -------------------------------------------------------------------------------- 1 | """Tests for HTML generator.""" 2 | 3 | import re 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_html, gen_integer 8 | 9 | 10 | # pylint disable:W0621 11 | @pytest.fixture 12 | def matcher(): 13 | """Instantiate a factory and compile a regex. 14 | 15 | The compiled regex can be used to find the contents of an HTML tag. 16 | """ 17 | return re.compile("^<.*?>(.*?)$") 18 | 19 | 20 | def test_length_arg_omitted(matcher): 21 | """Generate a random HTML tag with no ``length`` argument.""" 22 | match = matcher.search(gen_html()) 23 | assert len(match.group(1)) >= 1 24 | 25 | 26 | def test_length_arg_provided(matcher): 27 | """Generate a random HTML tag with ``length`` argument.""" 28 | length = gen_integer(1, 25) 29 | match = matcher.search(gen_html(length)) 30 | assert len(match.group(1)) == length 31 | 32 | 33 | def test_unicode(): 34 | """Generate a random HTML tag.""" 35 | assert isinstance(gen_html(), str) 36 | 37 | 38 | # pylint: disable=C0103 39 | def test_generate_html_with_len_less_than_min(): 40 | """Cannot generate a HTML string with length less than minimum.""" 41 | for value in range(8): 42 | with pytest.raises(ValueError): 43 | gen_html(value, include_tags=False) 44 | 45 | 46 | @pytest.mark.parametrize("length", [8, 10, 12, 20, 100]) 47 | def test_generate_html_with_len_more_than_min(length): 48 | """Cannot generate a HTML string with length more than minimum.""" 49 | assert length == len(gen_html(length, include_tags=False)) 50 | -------------------------------------------------------------------------------- /tests/test_ipaddress.py: -------------------------------------------------------------------------------- 1 | """Tests for ipaddr generator.""" 2 | 3 | import pytest 4 | 5 | from fauxfactory import gen_ipaddr 6 | 7 | 8 | def test_gen_ipv4_1(): 9 | """Generate a 3 group IPv4 address.""" 10 | result = gen_ipaddr(ip3=True) 11 | assert result.split(".")[-1] == "0" 12 | 13 | 14 | def test_gen_ipv4_2(): 15 | """Generate a 4 group IPv4 address.""" 16 | result = gen_ipaddr() 17 | assert len(result.split(".")) == 4 18 | 19 | 20 | def test_gen_ipv4_3(): 21 | """Generate a 4 group IPv4 address.""" 22 | result = gen_ipaddr(ip3=False) 23 | assert len(result.split(".")) == 4 24 | 25 | 26 | def test_gen_ipv4_4(): 27 | """Generate a 4 group IPv4 address.""" 28 | result = gen_ipaddr(ip3=False, ipv6=False) 29 | assert len(result.split(".")) == 4 30 | 31 | 32 | def test_gen_ipv4_5(): 33 | """Generate a 4 group IPv4 address with good prefix.""" 34 | result = gen_ipaddr(ip3=False, ipv6=False, prefix=[10]) 35 | assert len(result.split(".")) == 4 36 | assert result.startswith("10.") 37 | 38 | 39 | def test_gen_ipv4_6(): 40 | """Generate a 4 group IPv4 address with good prefix.""" 41 | result = gen_ipaddr(ip3=False, ipv6=False, prefix=[10, 10]) 42 | assert len(result.split(".")) == 4 43 | assert result.startswith("10.10.") 44 | 45 | 46 | def test_gen_ipv4_7(): 47 | """Generate a 4 group IPv4 address with good prefix.""" 48 | result = gen_ipaddr(ip3=False, ipv6=False, prefix=[10, 10, 10]) 49 | assert len(result.split(".")) == 4 50 | assert result.startswith("10.10.10.") 51 | 52 | 53 | def test_gen_ipv4_8(): 54 | """Generate a 4 group IPv4 address with prefix disabling randomness.""" 55 | with pytest.raises(ValueError): 56 | gen_ipaddr(ip3=False, ipv6=False, prefix=[10, 10, 10, 10]) 57 | 58 | 59 | def test_gen_ipv4_9(): 60 | """Generate a 4 group IPv4 address with prefix too long.""" 61 | with pytest.raises(ValueError): 62 | gen_ipaddr(ip3=False, ipv6=False, prefix=[10, 10, 10, 10, 10]) 63 | 64 | 65 | def test_gen_ipv4_10(): 66 | """Generate a 3 group IPv4 address with good prefix.""" 67 | result = gen_ipaddr(ip3=True, ipv6=False, prefix=[10, 10]) 68 | assert len(result.split(".")) == 4 69 | assert result.startswith("10.10.") 70 | assert result.endswith(".0") 71 | 72 | 73 | def test_gen_ipv4_11(): 74 | """Generate a 3 group IPv4 address with prefix disabling randomness.""" 75 | with pytest.raises(ValueError): 76 | gen_ipaddr(ip3=True, ipv6=False, prefix=[10, 10, 10]) 77 | 78 | 79 | def test_gen_ipv4_12(): 80 | """Generate a 3 group IPv4 address with prefix too long.""" 81 | with pytest.raises(ValueError): 82 | gen_ipaddr(ip3=True, ipv6=False, prefix=[10, 10, 10, 10]) 83 | 84 | 85 | def test_gen_ipv6_1(): 86 | """Generate a IPv6 address.""" 87 | result = gen_ipaddr(ipv6=True) 88 | assert len(result.split(":")) == 8 89 | 90 | 91 | def test_gen_ipv6_2(): 92 | """Generate a IPv6 address.""" 93 | result = gen_ipaddr(ip3=True, ipv6=True) 94 | assert len(result.split(":")) == 8 95 | 96 | 97 | def test_gen_ipv6_3(): 98 | """Generate a IPv6 address with custom prefix.""" 99 | result = gen_ipaddr(ipv6=True, prefix=["e2d3"]) 100 | assert len(result.split(":")) == 8 101 | assert result.startswith("e2d3:") 102 | 103 | 104 | def test_gen_ipv6_4(): 105 | """Generate a IPv6 address with custom (very long) prefix.""" 106 | prefix = 7 * ["e2d3"] 107 | result = gen_ipaddr(ipv6=True, prefix=prefix) 108 | assert len(result.split(":")) == 8 109 | assert result.startswith(":".join(prefix)) 110 | 111 | 112 | def test_gen_ipv6_5(): 113 | """Generate a IPv6 address with too long prefix.""" 114 | prefix = 8 * ["e2d3"] 115 | with pytest.raises(ValueError): 116 | gen_ipaddr(ipv6=True, prefix=prefix) 117 | 118 | 119 | def test_gen_ipv6_6(): 120 | """Generate a IPv6 address with even longer prefix.""" 121 | prefix = 9 * ["e2d3"] 122 | with pytest.raises(ValueError): 123 | gen_ipaddr(ipv6=True, prefix=prefix) 124 | -------------------------------------------------------------------------------- /tests/test_lorem_ipsum.py: -------------------------------------------------------------------------------- 1 | """Tests for Lorem Ipsum generator.""" 2 | 3 | import random 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_iplum 8 | from fauxfactory.constants import LOREM_IPSUM_TEXT 9 | 10 | 11 | def test_gen_loremipsum_1(): 12 | """Create a complete lorem ipsum string.""" 13 | result = gen_iplum() 14 | assert result == LOREM_IPSUM_TEXT 15 | assert result.startswith("Lorem ipsum") 16 | 17 | 18 | def test_gen_loremipsum_2(): 19 | """Create a lorem ipsum string with fixed number of words.""" 20 | for _ in range(20): 21 | length = random.randint(1, 500) 22 | result = gen_iplum(words=length) 23 | assert len(result.split()) == length 24 | 25 | 26 | def test_gen_loremipsum_3(): 27 | """Create a lorem ipsum string with fixed number of paragraphs.""" 28 | for _ in range(20): 29 | length = random.randint(1, 20) 30 | result = gen_iplum(paragraphs=length) 31 | assert len(result.split("\n")) == length 32 | 33 | 34 | def test_gen_loremipsum_4(): 35 | """Create a lorem ipsum string with zero words.""" 36 | result = gen_iplum(words=0) 37 | assert result == LOREM_IPSUM_TEXT 38 | 39 | 40 | def test_gen_loremipsum_5(): 41 | """Create a lorem ipsum string with zero paragraphs.""" 42 | with pytest.raises(ValueError): 43 | gen_iplum(paragraphs=0) 44 | 45 | 46 | def test_gen_loremipsum_6(): 47 | """Create a lorem ipsum string with 1 word and 0 paragragh.""" 48 | with pytest.raises(ValueError): 49 | gen_iplum(words=1, paragraphs=0) 50 | 51 | 52 | def test_gen_loremipsum_7(): 53 | """Create a lorem ipsum string with 1 word and 1 paragragh.""" 54 | result = gen_iplum(words=1, paragraphs=1) 55 | assert len(result.split()) == 1 56 | assert len(result.split()) == 1 57 | 58 | 59 | def test_gen_loremipsum_8(): 60 | """Create a lorem ipsum string with non-integer words.""" 61 | with pytest.raises(ValueError): 62 | gen_iplum(words="a") 63 | 64 | 65 | def test_gen_loremipsum_9(): 66 | """Create a lorem ipsum string with non-integer paragraphs.""" 67 | with pytest.raises(ValueError): 68 | gen_iplum(paragraphs="a") 69 | 70 | 71 | def test_gen_loremipsum_10(): 72 | """Create a lorem ipsum string with random words/paragraphs.""" 73 | for _ in range(20): 74 | words = random.randint(1, 500) 75 | paragraphs = random.randint(1, 500) 76 | result = gen_iplum(words=words, paragraphs=paragraphs) 77 | assert len(result.split("\n")) == paragraphs 78 | for sentence in result.split("\n"): 79 | assert len(sentence.split()) == words 80 | -------------------------------------------------------------------------------- /tests/test_macs.py: -------------------------------------------------------------------------------- 1 | """Tests for MAC generator.""" 2 | 3 | import random 4 | import re 5 | import string 6 | 7 | import pytest 8 | 9 | from fauxfactory import gen_mac 10 | 11 | MAC = re.compile("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$") 12 | 13 | 14 | def test_gen_mac_1(): 15 | r"""Generate a MAC address using \":\" as the delimiter.""" 16 | result = gen_mac() 17 | assert len(result.split(":")) == 6 18 | assert MAC.match(result) is not None 19 | 20 | 21 | def test_gen_mac_2(): 22 | r"""Generate a MAC address using \":\" as the delimiter.""" 23 | result = gen_mac(delimiter=":") 24 | assert len(result.split(":")) == 6 25 | assert MAC.match(result) is not None 26 | 27 | 28 | def test_gen_mac_3(): 29 | r"""Generate a MAC address using \"-\" as the delimiter.""" 30 | result = gen_mac(delimiter="-") 31 | assert len(result.split("-")) == 6 32 | assert MAC.match(result) is not None 33 | 34 | 35 | def test_gen_mac_4(): 36 | r"""Generate a MAC address using \".\" as the delimiter.""" 37 | with pytest.raises(ValueError): 38 | gen_mac(delimiter=".") 39 | 40 | 41 | def test_gen_mac_5(): 42 | r"""Generate a MAC address using \" \" as the delimiter.""" 43 | with pytest.raises(ValueError): 44 | gen_mac(delimiter=" ") 45 | 46 | 47 | def test_gen_mac_6(): 48 | """Generate a MAC address using a number as the delimiter.""" 49 | with pytest.raises(ValueError): 50 | gen_mac(delimiter=random.randint(0, 10)) 51 | 52 | 53 | def test_gen_mac_7(): 54 | """Generate a MAC address using a letter as the delimiter.""" 55 | with pytest.raises(ValueError): 56 | gen_mac(delimiter=random.choice(string.ascii_letters)) 57 | 58 | 59 | # pylint: disable=C0103 60 | def test_gen_mac_unicast_globally_unique(): 61 | """Generate a unicast and globally unique MAC address.""" 62 | mac = gen_mac(multicast=False, locally=False) 63 | first_octect = int(mac.split(":", 1)[0], 16) 64 | mask = 0b00000011 65 | assert first_octect & mask == 0 66 | 67 | 68 | def test_gen_mac_multicast_globally_unique(): 69 | """Generate a multicast and globally unique MAC address.""" 70 | mac = gen_mac(multicast=True, locally=False) 71 | first_octect = int(mac.split(":", 1)[0], 16) 72 | mask = 0b00000011 73 | assert first_octect & mask == 1 74 | 75 | 76 | def test_gen_mac_unicast_locally_administered(): 77 | """Generate a unicast and locally administered MAC address.""" 78 | mac = gen_mac(multicast=False, locally=True) 79 | first_octect = int(mac.split(":", 1)[0], 16) 80 | mask = 0b00000011 81 | assert first_octect & mask == 2 82 | 83 | 84 | def test_gen_mac_multicast_locally_administered(): 85 | """Generate a multicast and locally administered MAC address.""" 86 | mac = gen_mac(multicast=True, locally=True) 87 | first_octect = int(mac.split(":", 1)[0], 16) 88 | mask = 0b00000011 89 | assert first_octect & mask == 3 90 | -------------------------------------------------------------------------------- /tests/test_netmasks.py: -------------------------------------------------------------------------------- 1 | """Tests for the netmask generator.""" 2 | 3 | import re 4 | 5 | import pytest 6 | 7 | from fauxfactory import gen_netmask 8 | from fauxfactory.constants import VALID_NETMASKS 9 | 10 | NETMASK_REGEX = re.compile( 11 | "((255.){3}(0|128|192|224|240|248|252|254|255))|" 12 | "((255.){2}(0|128|192|224|240|248|252|254).0)|" 13 | "(255.(0|128|192|224|240|248|252|254)(.0){2})|" 14 | "((0|128|192|224|240|248|252|254)(.0){3})" 15 | ) 16 | 17 | 18 | def test_gen_netmask(): 19 | """Test if gen_netmask generates valid values.""" 20 | result = gen_netmask() 21 | assert len(result.split(".")) == 4 22 | assert NETMASK_REGEX.match(result) is not None 23 | 24 | 25 | def test_gen_netmask_boundary(): 26 | """Test gen_netmask boundary cases.""" 27 | assert gen_netmask(0, 0) == "0.0.0.0" 28 | assert gen_netmask(32, 32) == "255.255.255.255" 29 | with pytest.raises(ValueError): 30 | gen_netmask(-1, 16) 31 | with pytest.raises(ValueError): 32 | gen_netmask(16, 33) 33 | 34 | 35 | def test_valid_netmasks(): 36 | """Test if VALID_NETMASKS constant have valid netmask values.""" 37 | for netmask in VALID_NETMASKS: 38 | assert NETMASK_REGEX.match(netmask) is not None 39 | -------------------------------------------------------------------------------- /tests/test_numbers.py: -------------------------------------------------------------------------------- 1 | """Tests for all number generators.""" 2 | 3 | import sys 4 | from collections import namedtuple 5 | from functools import partial 6 | 7 | import pytest 8 | 9 | from fauxfactory import ( 10 | gen_hexadecimal, 11 | gen_integer, 12 | gen_negative_integer, 13 | gen_number, 14 | gen_octagonal, 15 | gen_positive_integer, 16 | ) 17 | from fauxfactory.helpers import VALID_DIGITS, base_repr 18 | 19 | GenFuncDataSet = namedtuple("GenFuncDataSet", ["gen_func", "expect_type", "base"]) 20 | GenFuncDataSets = [ 21 | GenFuncDataSet(gen_hexadecimal, str, 16), 22 | GenFuncDataSet(gen_integer, int, 10), 23 | GenFuncDataSet(gen_octagonal, str, 8), 24 | GenFuncDataSet(partial(gen_number, base=2), str, 2), 25 | GenFuncDataSet(partial(gen_number, base=5), str, 5), 26 | GenFuncDataSet(partial(gen_number, base=19), str, 19), 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("base", [0, 1]) 31 | def test_base_repr_small_base(base): 32 | """Testing the base_repr helper.""" 33 | with pytest.raises(ValueError): 34 | base_repr(1, base) 35 | 36 | 37 | @pytest.mark.parametrize( 38 | "number, base, result", 39 | [ 40 | (10, 10, "10"), 41 | (1, 2, "1"), 42 | (7, 6, "11"), 43 | (16, 8, "20"), 44 | (3, 3, "10"), 45 | (21, 20, "11"), 46 | (123, 12, "a3"), 47 | (139, 16, "8b"), 48 | (0, 10, "0"), 49 | ], 50 | ) 51 | def test_base_repr(number, base, result): 52 | """Return base representation for a number.""" 53 | assert base_repr(number, base) == result 54 | 55 | 56 | @pytest.mark.parametrize("data_set", GenFuncDataSets) 57 | def test_gen_number_1(data_set): 58 | """Create a random number with no range limits.""" 59 | result = data_set.gen_func() 60 | assert isinstance(result, data_set.expect_type) 61 | assert set(str(result).lower()).issubset(set(VALID_DIGITS[: data_set.base] + "-")) 62 | 63 | 64 | @pytest.mark.parametrize("data_set", GenFuncDataSets) 65 | def test_gen_number_2(data_set): 66 | """Create a random number with set minimum limit.""" 67 | try: 68 | # Change system max int to a smaller number 69 | old_sys_maxsize = sys.maxsize 70 | sys.maxsize = 5 71 | 72 | for _ in range(10): 73 | result = int(str(data_set.gen_func(min_value=1)), base=data_set.base) 74 | assert result <= sys.maxsize 75 | assert result >= 1 76 | finally: 77 | # Reset system max int back to original value 78 | sys.maxsize = old_sys_maxsize 79 | 80 | 81 | @pytest.mark.parametrize("data_set", GenFuncDataSets) 82 | def test_gen_number_3(data_set): 83 | """Create a random number with set maximum limit.""" 84 | try: 85 | # Change system max int to a smaller number 86 | old_sys_maxsize = sys.maxsize 87 | sys.maxsize = 1000 88 | min_value = -sys.maxsize - 1 89 | 90 | max_value_based = ( 91 | 1000 if data_set.gen_func is gen_integer else base_repr(1000, data_set.base) 92 | ) 93 | for _ in range(10): 94 | result = int( 95 | str(data_set.gen_func(max_value=max_value_based)), base=data_set.base 96 | ) 97 | assert result >= min_value 98 | assert result <= 1000 99 | finally: 100 | # Reset system max int back to original value 101 | sys.maxsize = old_sys_maxsize 102 | 103 | 104 | @pytest.mark.parametrize("data_set", GenFuncDataSets) 105 | def test_gen_number_4(data_set): 106 | """Create a random number with set min/max limits.""" 107 | max_value_based = ( 108 | 3000 if data_set.gen_func is gen_integer else base_repr(3000, data_set.base) 109 | ) 110 | for _ in range(10): 111 | result = int( 112 | str(data_set.gen_func(min_value=1, max_value=max_value_based)), 113 | base=data_set.base, 114 | ) 115 | assert result >= 1 116 | assert result <= 3000 117 | 118 | 119 | def test_gen_integer_1(): 120 | """Create a random integer with disallowed minimum limit.""" 121 | # This is lower than allowed platform minimum 122 | low_min = -sys.maxsize - 2 123 | 124 | with pytest.raises(ValueError): 125 | gen_integer(min_value=low_min) 126 | 127 | 128 | def test_gen_integer_2(): 129 | """Create a random integer with disallowed maximum limit.""" 130 | # This is greater than allowed platform maximum 131 | high_max = sys.maxsize + 1 132 | 133 | with pytest.raises(ValueError): 134 | gen_integer(max_value=high_max) 135 | 136 | 137 | def test_gen_integer_7_0(): 138 | """Create a random integer using empty strings as args.""" 139 | with pytest.raises(ValueError): 140 | gen_integer(min_value="") 141 | 142 | 143 | def test_gen_integer_7_1(): 144 | """Create a random integer using empty strings as args.""" 145 | with pytest.raises(ValueError): 146 | gen_integer(max_value="") 147 | 148 | 149 | def test_gen_integer_7_2(): 150 | """Create a random integer using empty strings as args.""" 151 | with pytest.raises(ValueError): 152 | gen_integer(min_value="", max_value="") 153 | 154 | 155 | def test_gen_integer_8_0(): 156 | """Create a random integer using whitespace as args.""" 157 | with pytest.raises(ValueError): 158 | gen_integer(min_value=" ") 159 | 160 | 161 | def test_gen_integer_8_1(): 162 | """Create a random integer using whitespace as args.""" 163 | with pytest.raises(ValueError): 164 | gen_integer(max_value=" ") 165 | 166 | 167 | def test_gen_integer_8_2(): 168 | """Create a random integer using whitespace as args.""" 169 | with pytest.raises(ValueError): 170 | gen_integer(min_value=" ", max_value=" ") 171 | 172 | 173 | def test_gen_integer_9_0(): 174 | """Create a random integer using alpha strings as args.""" 175 | with pytest.raises(ValueError): 176 | gen_integer(min_value="a") 177 | 178 | 179 | def test_gen_integer_9_1(): 180 | """Create a random integer using alpha strings as args.""" 181 | with pytest.raises(ValueError): 182 | gen_integer(max_value="a") 183 | 184 | 185 | def test_gen_integer_9_2(): 186 | """Create a random integer using alpha strings as args.""" 187 | with pytest.raises(ValueError): 188 | gen_integer(min_value="a", max_value="b") 189 | 190 | 191 | def test_gen_positive_integer_1(): 192 | """Create a random positive integer.""" 193 | assert gen_positive_integer() >= 0 194 | 195 | 196 | def test_gen_negative_integer_1(): 197 | """Create a random negative integer.""" 198 | assert gen_negative_integer() <= 0 199 | -------------------------------------------------------------------------------- /tests/test_strings.py: -------------------------------------------------------------------------------- 1 | """Tests for all string generators.""" 2 | 3 | import string 4 | import unicodedata 5 | from random import randint 6 | 7 | import pytest 8 | 9 | from fauxfactory import ( 10 | gen_alpha, 11 | gen_alphanumeric, 12 | gen_cjk, 13 | gen_cyrillic, 14 | gen_html, 15 | gen_latin1, 16 | gen_numeric_string, 17 | gen_special, 18 | gen_string, 19 | gen_utf8, 20 | ) 21 | from fauxfactory.helpers import BMP, unicode_letters_generator 22 | 23 | GENERATORS = [ 24 | gen_html, 25 | gen_alpha, 26 | gen_alphanumeric, 27 | gen_cjk, 28 | gen_cyrillic, 29 | gen_latin1, 30 | gen_numeric_string, 31 | gen_utf8, 32 | gen_special, 33 | ] 34 | 35 | STRING_TYPES = [ 36 | "html", 37 | "alpha", 38 | "alphanumeric", 39 | "cjk", 40 | "cyrillic", 41 | "latin1", 42 | "numeric", 43 | "utf8", 44 | "punctuation", 45 | ] 46 | 47 | 48 | @pytest.mark.parametrize("fnc", GENERATORS) 49 | def test_positive_string(fnc): 50 | """Default string generated is longer than zero characters.""" 51 | assert fnc() 52 | 53 | 54 | @pytest.mark.parametrize("fnc", GENERATORS[1:]) 55 | def test_fixed_length_positional(fnc): 56 | """String generated has correct length of characters.""" 57 | assert len(fnc(10)) == 10 58 | 59 | 60 | @pytest.mark.parametrize("fnc", GENERATORS[1:]) 61 | def test_fixed_length_keyword(fnc): 62 | """String generated has correct length of characters.""" 63 | assert len(fnc(length=10)) == 10 64 | 65 | 66 | @pytest.mark.parametrize("fnc", GENERATORS) 67 | def test_negative_length(fnc): 68 | """Cannot generate string with negative length of characters.""" 69 | with pytest.raises(ValueError): 70 | fnc(-1) 71 | 72 | 73 | @pytest.mark.parametrize("fnc", GENERATORS) 74 | def test_zero_length(fnc): 75 | """Cannot generate string with zero length of characters.""" 76 | with pytest.raises(ValueError): 77 | fnc(0) 78 | 79 | 80 | @pytest.mark.parametrize("fnc", GENERATORS) 81 | def test_alpha_length(fnc): 82 | """Cannot generate string with alpha length of characters.""" 83 | with pytest.raises(ValueError): 84 | fnc("a") 85 | 86 | 87 | @pytest.mark.parametrize("fnc", GENERATORS) 88 | def test_alphanumeric_length(fnc): 89 | """Cannot generate string with alphanumeric length of characters.""" 90 | with pytest.raises(ValueError): 91 | fnc("-1") 92 | 93 | 94 | @pytest.mark.parametrize("fnc", GENERATORS) 95 | def test_empty_length(fnc): 96 | """Cannot generate string with empty length of characters.""" 97 | with pytest.raises(ValueError): 98 | fnc("") 99 | 100 | 101 | @pytest.mark.parametrize("fnc", GENERATORS) 102 | def test_space_length(fnc): 103 | """Cannot generate string with space length of characters.""" 104 | with pytest.raises(ValueError): 105 | fnc(" ") 106 | 107 | 108 | @pytest.mark.parametrize("fnc", STRING_TYPES) 109 | def test_gen_string(fnc): 110 | """Use `gen_string` to generate supported string.""" 111 | assert gen_string(fnc) 112 | 113 | 114 | # pylint: disable=invalid-name 115 | @pytest.mark.parametrize("fnc", STRING_TYPES[1:]) 116 | def test_gen_string_fixed_length_positional(fnc): 117 | """Use `gen_string` to generate supported string with expected length.""" 118 | assert len(gen_string(fnc, 5)) == 5 119 | 120 | 121 | # pylint: disable=invalid-name 122 | @pytest.mark.parametrize("fnc", STRING_TYPES[1:]) 123 | def test_gen_string_fixed_length_keyword(fnc): 124 | """Use `gen_string` to generate supported string with explict `length`.""" 125 | assert len(gen_string(fnc, length=5)) == 5 126 | 127 | 128 | def test_chars_in_letters_category(): 129 | """Unicode letters generator generates only unicode letters.""" 130 | # Categories extracted from section 5.5.1 of 131 | # http://www.unicode.org/reports/tr44/tr44-4.html 132 | for char in unicode_letters_generator(): 133 | assert unicodedata.category(char) in ("Lu", "Ll", "Lt", "Lm", "Lo") 134 | 135 | 136 | def test_bmp_chars_only(): 137 | """Unicode letters generator generates only BMP unicode letters.""" 138 | for char in gen_utf8(length=50, smp=False): 139 | assert ord(char) <= BMP.max 140 | 141 | 142 | def test_invalid_string_type(): 143 | """Only valid string types can be generated.""" 144 | with pytest.raises(ValueError): 145 | gen_string("foo") 146 | 147 | 148 | def test_special_string(): 149 | """Assert that only punctuation strings are returned.""" 150 | VALID_CHARS = string.punctuation 151 | special_str = gen_special() 152 | for char in special_str: 153 | assert char in VALID_CHARS 154 | 155 | 156 | @pytest.mark.parametrize("fnc", GENERATORS[1:]) 157 | def test_start_string(fnc): 158 | """String generated has start with specific keyword.""" 159 | start = fnc(randint(1, 5)) 160 | separator = fnc(1) 161 | random_str = fnc(start=start, separator=separator) 162 | assert start == random_str[: len(start)] 163 | assert separator == random_str[len(start)] 164 | assert len(random_str) == 10 165 | -------------------------------------------------------------------------------- /tests/test_system_facts.py: -------------------------------------------------------------------------------- 1 | """Tests for system facts generator.""" 2 | 3 | import re 4 | 5 | from fauxfactory import gen_system_facts 6 | 7 | REGEX = r"^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$" 8 | 9 | 10 | def test_gen_system_facts(): 11 | """Create a random system facts.""" 12 | # Regex for domain validation 13 | validator = re.compile(REGEX) 14 | facts = gen_system_facts() 15 | 16 | assert validator.match(facts["fqdn"]) 17 | 18 | 19 | def test_gen_system_facts_with_name(): 20 | """Create a random system facts using a name.""" 21 | # Regex for domain validation 22 | facts = gen_system_facts(name="faux") 23 | 24 | assert facts["fqdn"].startswith("faux") 25 | assert facts["hostname"] == "faux" 26 | 27 | 28 | def test_gen_system_facts_with_name_subdomain(): 29 | """Create a random system facts using name and subdomain.""" 30 | # Regex for domain validation 31 | facts = gen_system_facts(name="faux.example") 32 | 33 | assert facts["fqdn"].startswith("faux.example") 34 | assert facts["hostname"] == "faux" 35 | assert facts["domain"].startswith("example") 36 | 37 | 38 | def test_gen_system_facts_with_fqdn(): 39 | """Create a random system facts using FQDN.""" 40 | # Regex for domain validation 41 | validator = re.compile(REGEX) 42 | facts = gen_system_facts(name="faux.example.com") 43 | 44 | assert validator.match(facts["fqdn"]) 45 | assert facts["fqdn"] == "faux.example.com" 46 | assert facts["domain"] == "example.com" 47 | assert facts["hostname"] == "faux" 48 | -------------------------------------------------------------------------------- /tests/test_system_helpers.py: -------------------------------------------------------------------------------- 1 | """Tests for system facts helpers.""" 2 | 3 | import pytest 4 | 5 | from fauxfactory.factories.systems import ( 6 | add_memory_info, 7 | add_network_devices, 8 | add_operating_system, 9 | add_partitions, 10 | add_processor_info, 11 | ) 12 | 13 | 14 | # Memory 15 | def test_add_random_memory(): 16 | """Get facts for random memory.""" 17 | facts = add_memory_info() 18 | total_memory_mb = int(facts["memorysize_mb"]) 19 | total_free_memory_mb = int(facts["memoryfree_mb"]) 20 | total_swap_memory_mb = int(facts["swapsize_mb"]) 21 | 22 | assert total_memory_mb >= (4 * 1024) 23 | assert total_memory_mb <= (128 * 1024) 24 | assert total_free_memory_mb <= total_memory_mb 25 | assert total_swap_memory_mb == total_memory_mb 26 | 27 | 28 | def test_add_specific_memory(): 29 | """Get facts for specific memory.""" 30 | facts = add_memory_info(count=32) 31 | total_memory_mb = int(facts["memorysize_mb"]) 32 | total_free_memory_mb = int(facts["memoryfree_mb"]) 33 | total_swap_memory_mb = int(facts["swapsize_mb"]) 34 | 35 | assert total_memory_mb == (32 * 1024) 36 | assert total_free_memory_mb <= total_memory_mb 37 | assert total_swap_memory_mb == total_memory_mb 38 | 39 | 40 | def test_add_zero_memory(): 41 | """Cannot add zero memory.""" 42 | with pytest.raises(ValueError): 43 | add_memory_info(count=0) 44 | 45 | 46 | def test_add_negative_memory(): 47 | """Cannot add negative memory.""" 48 | with pytest.raises(ValueError): 49 | add_memory_info(count=-1) 50 | 51 | 52 | # Network 53 | def test_add_network_devices(): 54 | """Get facts for network devices.""" 55 | facts = add_network_devices() 56 | assert facts["network_lo"] == "127.0.0.0" 57 | 58 | 59 | # Operating Systems 60 | def test_add_random_operating_system(): 61 | """Get facts for a random operating system.""" 62 | facts = add_operating_system() 63 | assert facts["operatingsystem"] 64 | assert facts["osfamily"] 65 | assert facts["os"]["release"]["major"] >= 0 66 | assert facts["os"]["release"]["major"] <= 9 67 | assert facts["os"]["release"]["minor"] >= 0 68 | assert facts["os"]["release"]["minor"] <= 9 69 | 70 | 71 | # Operating Systems 72 | def test_add_random_operating_system_with_attributes(): 73 | """Get facts for a random operating system with all attributes.""" 74 | facts = add_operating_system(name="Fedora", family="Red Hat", major=25, minor=1) 75 | assert facts["operatingsystem"] == "Fedora" 76 | assert facts["osfamily"] == "Red Hat" 77 | assert facts["os"]["release"]["major"] == 25 78 | assert facts["os"]["release"]["minor"] == 1 79 | 80 | 81 | # Partitions 82 | def test_add_single_partition(): 83 | """Get facts for a single partition.""" 84 | facts = add_partitions() 85 | assert "sda1" in facts["partitions"].keys() 86 | 87 | 88 | def test_add_three_partitions(): 89 | """Get facts for three partition.""" 90 | facts = add_partitions(extra_partitions=3) 91 | assert "sda1" in facts["partitions"].keys() 92 | assert "sdb1" in facts["partitions"].keys() 93 | assert "sdb2" in facts["partitions"].keys() 94 | assert "sdb3" in facts["partitions"].keys() 95 | 96 | 97 | def test_add_zero_extra_partition(): 98 | """Cannot add zero extra partitions.""" 99 | with pytest.raises(ValueError): 100 | add_partitions(extra_partitions=0) 101 | 102 | 103 | def test_add_negative_extra_partition(): 104 | """Cannot add negative extra partitions.""" 105 | with pytest.raises(ValueError): 106 | add_partitions(extra_partitions=-1) 107 | 108 | 109 | # Processors 110 | def test_add_random_processors(): 111 | """Get facts for random number of processors.""" 112 | facts = add_processor_info() 113 | assert facts["processorcount"] >= 2 114 | assert facts["processorcount"] <= 16 115 | assert len(facts["processors"]["models"]) >= 2 116 | assert len(facts["processors"]["models"]) <= 16 117 | 118 | 119 | def test_add_one_processor(): 120 | """Get facts for a single processor.""" 121 | facts = add_processor_info(count=1) 122 | assert facts["processorcount"] == 1 123 | assert len(facts["processors"]["models"]) == 1 124 | 125 | 126 | def test_add_two_processors(): 127 | """Get facts for two processors.""" 128 | facts = add_processor_info(count=2) 129 | assert facts["processorcount"] == 2 130 | assert len(facts["processors"]["models"]) == 2 131 | 132 | 133 | def test_add_zero_processor(): 134 | """Cannot add zero processors.""" 135 | with pytest.raises(ValueError): 136 | add_processor_info(count=0) 137 | 138 | 139 | def test_add_negative_processor(): 140 | """Cannot add negative processors.""" 141 | with pytest.raises(ValueError): 142 | add_processor_info(count=-1) 143 | -------------------------------------------------------------------------------- /tests/test_time.py: -------------------------------------------------------------------------------- 1 | """Tests for Time generator.""" 2 | 3 | import datetime 4 | 5 | from fauxfactory import gen_time 6 | 7 | 8 | def test_gen_uuid_1(): 9 | """Create a random UUID value.""" 10 | for _ in range(100): 11 | assert isinstance(gen_time(), datetime.time) 12 | -------------------------------------------------------------------------------- /tests/test_urls.py: -------------------------------------------------------------------------------- 1 | """Tests for URL generator.""" 2 | 3 | import pytest 4 | 5 | from fauxfactory import ( 6 | gen_alpha, 7 | gen_alphanumeric, 8 | gen_cjk, 9 | gen_numeric_string, 10 | gen_url, 11 | ) 12 | from fauxfactory.constants import SCHEMES 13 | 14 | 15 | def test_gen_url_1(): 16 | """Create a random URL.""" 17 | for _ in range(10): 18 | result = gen_url() 19 | assert result 20 | assert result.split(":")[0] in SCHEMES 21 | 22 | 23 | def test_gen_url_2(): 24 | """Create a random URL with http scheme.""" 25 | for _ in range(10): 26 | result = gen_url(scheme="http") 27 | assert result 28 | assert result.split(":")[0] == "http" 29 | 30 | 31 | def test_gen_url_3(): 32 | """Create a random URL with https scheme.""" 33 | for _ in range(10): 34 | result = gen_url(scheme="https") 35 | assert result 36 | assert result.split(":")[0] == "https" 37 | 38 | 39 | def test_gen_url_4(): 40 | """Create a random URL with ftp scheme.""" 41 | for _ in range(10): 42 | result = gen_url(scheme="ftp") 43 | assert result 44 | assert result.split(":")[0] == "ftp" 45 | 46 | 47 | def test_gen_url_5(): 48 | """Create a random URL with invalid scheme.""" 49 | for _ in range(10): 50 | scheme = gen_alphanumeric() 51 | with pytest.raises(ValueError): 52 | gen_url(scheme=scheme) 53 | 54 | 55 | def test_gen_url_6(): 56 | """Create a random URL with valid subdomain.""" 57 | for _ in range(10): 58 | subdomain = gen_alphanumeric() 59 | result = gen_url(subdomain=subdomain) 60 | assert result 61 | 62 | # Breakdown the generated URL 63 | scheme_breakdown = result.split("//") 64 | domain = scheme_breakdown[1].split(".") 65 | assert domain[0] == subdomain 66 | 67 | 68 | def test_gen_url_7(): 69 | """Create a random URL with empty subdomain.""" 70 | result = gen_url(subdomain="") 71 | assert result 72 | 73 | 74 | def test_gen_url_8(): 75 | """Create a random URL with whitespace subdomain.""" 76 | with pytest.raises(ValueError): 77 | gen_url(subdomain=" ") 78 | 79 | 80 | def test_gen_url_9(): 81 | """Create a random URL with invalid subdomain.""" 82 | for _ in range(10): 83 | subdomain = gen_cjk() 84 | with pytest.raises(ValueError): 85 | gen_url(subdomain=subdomain) 86 | 87 | 88 | def test_gen_url_10(): 89 | """Create a random URL with valid TLDS.""" 90 | for _ in range(10): 91 | tlds = gen_alpha(length=3) 92 | result = gen_url(tlds=tlds) 93 | assert result 94 | assert result.split(".")[-1] == tlds 95 | 96 | 97 | def test_gen_url_11(): 98 | """Create a random URL with numeric TLDS.""" 99 | for _ in range(10): 100 | with pytest.raises(ValueError): 101 | tlds = gen_numeric_string(length=3) 102 | gen_url(tlds=tlds) 103 | 104 | 105 | def test_gen_url_12(): 106 | """Create a random URL with whitespace TLDS.""" 107 | for _ in range(10): 108 | with pytest.raises(ValueError): 109 | gen_url(tlds=" ") 110 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | """Unittests for all methods found in `utils.py`.""" 2 | 3 | import pytest 4 | 5 | from fauxfactory.helpers import is_positive_int 6 | 7 | 8 | @pytest.mark.parametrize("length", [1, 2, 3, 30, 100]) 9 | def test_positive_value(length): 10 | """Positive values are allowed.""" 11 | assert is_positive_int(length) is None 12 | 13 | 14 | @pytest.mark.parametrize("length", [-1, -2, -3, -30, -100]) 15 | def test_negative_value(length): 16 | """Negative values are not allowed.""" 17 | with pytest.raises(ValueError): 18 | is_positive_int(length) 19 | 20 | 21 | def test_zero_value(): 22 | """Zero is not an allowed value.""" 23 | with pytest.raises(ValueError): 24 | is_positive_int(0) 25 | 26 | 27 | def test_none_value(): 28 | """None is not an allowed value.""" 29 | with pytest.raises(ValueError): 30 | is_positive_int(None) 31 | 32 | 33 | def test_empty_value(): 34 | """Empty list is not an allowed value.""" 35 | with pytest.raises(ValueError): 36 | is_positive_int([]) 37 | 38 | 39 | @pytest.mark.parametrize("length", ["a", "foo", " ", "1", "0", "-1"]) 40 | def test_non_numeric_value(length): 41 | """Non-numeric values are not allowed.""" 42 | with pytest.raises(ValueError): 43 | is_positive_int(length) 44 | -------------------------------------------------------------------------------- /tests/test_uuids.py: -------------------------------------------------------------------------------- 1 | """Tests for UUID generator.""" 2 | 3 | from fauxfactory import gen_uuid 4 | 5 | 6 | def test_gen_uuid_1(): 7 | """Create a random UUID4 value.""" 8 | for _ in range(100): 9 | assert gen_uuid() 10 | --------------------------------------------------------------------------------