Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/issue 11 start pytest testing #16

Merged
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d68c700
add hypothesis configuration + basic range and amount tests
frank113 Jun 20, 2023
97972b5
Outsource strategies to strategies.py
frank113 Jun 21, 2023
4a17435
update pytest cli args
frank113 Jun 25, 2023
359f279
Update strategies and implementations in test_amount
frank113 Jun 25, 2023
6674519
adding tests for or
frank113 Jun 25, 2023
b6b67ed
add default hypothesis profile for 500 runs each
frank113 Jun 26, 2023
1e3de01
add unit tests for set
frank113 Jun 26, 2023
ee9759e
update build_amount to use optional stpe
frank113 Jun 26, 2023
c030d59
make bounds test more specific
frank113 Jun 26, 2023
ab5f09f
add unit tests for join function
frank113 Jun 26, 2023
7022842
update setup.cfg with new pattern
frank113 Jun 26, 2023
d858def
add dev installation with linters and testing packages
frank113 Jun 26, 2023
f1e41e1
update strategies docstring
frank113 Jun 26, 2023
7720236
move build_amount to test_amount
frank113 Jun 26, 2023
aeb47ad
format strategies
frank113 Jun 26, 2023
56fd449
Update formatting of test_join
frank113 Jun 26, 2023
2451396
update formatting in conftest
frank113 Jun 26, 2023
2ca1463
cleanup or test_or
frank113 Jun 26, 2023
18bcf21
create non_escape_char method and use in test_set
frank113 Jun 26, 2023
fdcb820
hotfix test_set
frank113 Jun 26, 2023
b958e5a
formatting test_range
frank113 Jun 26, 2023
f9e6411
update imports of test_amount
frank113 Jun 26, 2023
9da2ed0
black formatting
frank113 Jun 26, 2023
120b3b2
rename test_range for clarity
frank113 Jun 26, 2023
47fcbad
add testing workflow
frank113 Jun 26, 2023
aa604ec
specify workflow version
frank113 Jun 26, 2023
18dba4d
reference master branch in nested workflow
frank113 Jun 26, 2023
4cab0df
add trigger for styling for dispatch in future releases
frank113 Jun 26, 2023
c9ac08d
Use local pathing for reference styling workflow within main.yml
frank113 Jun 26, 2023
eab2f49
remove env variables from main workflow
frank113 Jun 26, 2023
f5654a3
Merge branch 'bugfix/pass_flake8_linting' into feature/issue-11_start…
frank113 Jun 26, 2023
5d15425
use python3.x
frank113 Jun 26, 2023
457aab1
Merge branch 'bugfix/pass_flake8_linting' into feature/issue-11_start…
frank113 Jun 26, 2023
5718dc1
add imports of ValidPatternType and ESCAPED_CHARACTERS to package exp…
frank113 Jun 26, 2023
7f707d9
Merge branch 'bugfix/pass_flake8_linting' into feature/issue-11_start…
frank113 Jun 26, 2023
0c687c4
Merge branch 'master' into feature/issue-11_start_pytest_testing
frank113 Jun 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI Testing

on:
push:
pull_request:

workflow_dispatch:

jobs:
testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.x'

- name: Install deps
run: |
pip3 --version
pip3 install .[dev]

- name: Run tests
run: pytest

code_style_checking:
uses: ./.github/workflows/styling.yml
1 change: 1 addition & 0 deletions .github/workflows/styling.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
paths:
- "**.py"
workflow_dispatch:
workflow_call:

jobs:
code_style_checking:
Expand Down
2 changes: 1 addition & 1 deletion regexfactory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
WHITESPACE,
WORD,
)
from .pattern import RegexPattern, escape, join
from .pattern import ESCAPED_CHARACTERS, RegexPattern, ValidPatternType, escape, join
from .patterns import (
Amount,
Comment,
Expand Down
17 changes: 17 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,23 @@ classifiers =
zip_safe = True
packages = regexfactory

[options.extras_require]
dev =
isort
black
flake8
pylint
pytest
hypothesis

[tool:pytest]
markers =
patterns: Tests for classes in regexfactory/patterns.py
pattern: Tests for classes in regexfactory/pattern.py
addopts = -ra --hypothesis-show-statistics --hypothesis-profile=default
testpaths =
tests

[flake8]
ignore = E501
exclude = .git, __pycache__, .github
Expand Down
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from hypothesis import settings

# Set default profile to use 500 examples
settings.register_profile("default", max_examples=500)
22 changes: 22 additions & 0 deletions tests/strategies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from hypothesis import strategies as st

from regexfactory.pattern import ESCAPED_CHARACTERS

# Strategy to generate characters that are not used in escapes
non_escape_char = st.characters(blacklist_characters=list(ESCAPED_CHARACTERS))


# Strategy to generate text that avoids escaped characters
non_escaped_text = st.text(min_size=1, alphabet=non_escape_char)


# Strategy to produce either None or a positive integer
optional_step = st.one_of(st.none(), st.integers(min_value=1))


def build_bounds(lower_bound, step) -> range:
"""
Function to generate a tuple of (lower, upper) in which lower < upper
"""
upper_bound = lower_bound + step
return range(lower_bound, upper_bound + 1)
89 changes: 89 additions & 0 deletions tests/test_amount.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from typing import Optional

import pytest
from hypothesis import given
from hypothesis import strategies as st
from strategies import build_bounds, optional_step

from regexfactory import Amount, ValidPatternType


def build_amount(
pattern: ValidPatternType,
start: int,
or_more: bool,
greedy: bool,
step: Optional[int],
):
"""
General Amount builder. Note that the `j` parameter is constructed as
the step plus start when step is defined. When step is None we assume
that no upper bound is present.
"""
stop_value = None
if step is not None:
stop_value = start + step
return Amount(
pattern=pattern, i=start, j=stop_value, or_more=or_more, greedy=greedy
)


@pytest.mark.patterns
@given(st.text(min_size=1), st.integers(min_value=1))
def test_amount_single_count(word, count):
"""
Test to ensure that when `or_more=False` and no upper bound is
provided the regex will be of the form word{count}.
"""
actual = Amount(word, i=count, or_more=False)
assert actual.regex == "{word}{{{count}}}".format(word=word, count=str(count))


@pytest.mark.patterns
@given(
st.text(min_size=1),
st.builds(
build_bounds,
lower_bound=st.integers(min_value=1),
step=st.integers(min_value=1),
),
)
def test_amount_lower_upper(word, bound: range):
"""
Test to ensure that if a lower and upper bound are provided then the
regex of the resulting `Amount` will be of the form {word}{lower,upper}.
"""
actual = Amount(word, bound.start, bound.stop)
expected = "{word}{{{lower},{upper}}}".format(
word=word, lower=str(bound.start), upper=str(bound.stop)
)
assert actual.regex == expected


@pytest.mark.patterns
@given(st.text(min_size=1), st.integers(min_value=1))
def test_amount_or_more(word, count):
"""
Test to ensure that when `or_more=True` and no upper bound is
provided the regex will be of the form word{count,}.
"""
actual = Amount(word, count, or_more=True)
assert actual.regex == "{word}{{{count},}}".format(word=word, count=str(count))


@pytest.mark.patterns
@given(
st.builds(
build_amount,
pattern=st.text(min_size=1),
start=st.integers(min_value=1),
or_more=st.booleans(),
greedy=st.just(False),
step=optional_step,
)
)
def test_amount_non_greedy(amt):
"""
Test to ensure that instances of Amount with greedy as False will end with "?"
"""
assert amt.regex.endswith("?")
18 changes: 18 additions & 0 deletions tests/test_join.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest
from hypothesis import example, given
from hypothesis import strategies as st
from strategies import non_escaped_text

from regexfactory.pattern import join


@pytest.mark.pattern
@given(st.lists(elements=non_escaped_text, min_size=1, max_size=10, unique=True))
@example(words=["0", "1"])
def test_join(words: list):
"""
Tests to capture that the join function concatenates the expressions and
each word in the list is found in the larger regex.
"""
joined_regex = join(*words)
assert joined_regex.regex == "".join(words)
26 changes: 26 additions & 0 deletions tests/test_or.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import re

import pytest
from hypothesis import example, given
from hypothesis import strategies as st
from strategies import non_escaped_text

from regexfactory import Or


@pytest.mark.patterns
@given(
st.lists(
non_escaped_text,
min_size=1,
max_size=10,
)
)
@example(arr=["0", "0"])
def test_matching_or(arr: list):
actual = Or(*arr)
if len(arr) == 1:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this if statement and simplify everything to just the for loop because you do the same thing in both cases, I think.

assert isinstance(actual.match(arr[0]), re.Match)
else:
for value in arr:
assert isinstance(actual.match(value), re.Match)
25 changes: 25 additions & 0 deletions tests/test_range.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import pytest

from regexfactory import Range


@pytest.mark.patterns
def test_numeric_range():
start = "0"
end = "9"
assert Range(start, end).regex == "[0-9]"


@pytest.mark.patterns
@pytest.mark.parametrize(
"start, stop, expected",
[
("0", "9", "[0-9]"),
("a", "f", "[a-f]"),
("r", "q", "[r-q]"),
("A", "Z", "[A-Z]"),
],
)
def test_range_parameters(start, stop, expected):
actual = Range(start=start, stop=stop)
assert actual.regex == expected
16 changes: 16 additions & 0 deletions tests/test_set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import re

import pytest
from hypothesis import given
from hypothesis import strategies as st
from strategies import non_escape_char

from regexfactory import Set


@pytest.mark.patterns
@given(st.lists(elements=non_escape_char, min_size=1))
def test_set(chars: list):
actual = Set(*chars)
for value in chars:
assert isinstance(actual.match(value), re.Match)
Loading