Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 12 additions & 1 deletion gator/checks/check_MatchCommandFragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ def get_parser():
action="store_true",
)

# IGNORE-CASE: match fragments without regard for letter case
# REQUIRED? No
optional_group.add_argument(
"--ignore-case",
help="match fragments ignoring letter case",
default=False,
action="store_true",
)

# }}}
return parser

Expand All @@ -80,6 +89,7 @@ def act(main_parsed_arguments, check_remaining_arguments):
# --> fragment is the content that should appear in the command's output
# --> count is required to specify the number of fragments to appear in the output
# --> exact is optional, but will either be True or False and False by default
# --> ignore-case is optional, enabling case-insensitive fragment matching
check_parsed_arguments = parse(check_remaining_arguments)
# Directly run the check since at least one of the argument's for it is mandatory.
# This means that the use of check_MatchCommandFragment would have already failed by this
Expand All @@ -88,6 +98,7 @@ def act(main_parsed_arguments, check_remaining_arguments):
fragment = check_parsed_arguments.fragment
count = check_parsed_arguments.count
exact = check_parsed_arguments.exact
ignore_case = check_parsed_arguments.ignore_case
return invoke.invoke_all_command_fragment_checks(
command, fragment, count, exact
command, fragment, count, exact, ignore_case
)
19 changes: 18 additions & 1 deletion gator/checks/check_MatchFileFragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ def get_parser():
action="store_true",
)

# IGNORE-CASE: match fragments without regard for letter case
# REQUIRED? No
optional_group.add_argument(
"--ignore-case",
help="match fragments ignoring letter case",
default=False,
action="store_true",
)

# }}}
return parser

Expand All @@ -87,6 +96,7 @@ def act(main_parsed_arguments, check_remaining_arguments):
# --> directory is the name of the directory that should contain the specified file
# --> count is required to specify the number of fragments to appear in the output
# --> exact is optional, but will either be True or False and False by default
# --> ignore-case is optional, enabling case-insensitive fragment matching
check_parsed_arguments = parse(check_remaining_arguments)
# Directly run the check since at least one of the argument's for it is mandatory.
# This means that the use of check_MatchFileFragment would have already failed by this
Expand All @@ -96,6 +106,13 @@ def act(main_parsed_arguments, check_remaining_arguments):
file = check_parsed_arguments.file
directory = check_parsed_arguments.directory
exact = check_parsed_arguments.exact
ignore_case = check_parsed_arguments.ignore_case
return invoke.invoke_all_fragment_checks(
fragment, count, file, directory, constants.markers.Nothing, exact
fragment,
count,
file,
directory,
constants.markers.Nothing,
exact,
ignore_case,
)
30 changes: 25 additions & 5 deletions gator/fragments.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,14 @@ def count_total_words(contents):
return count_words(contents, sum)


def count_specified_fragment(contents, fragment):
def count_specified_fragment(contents, fragment, ignore_case=False):
"""Count the specified string fragment in the string contents."""
fragment_count = contents.count(fragment)
# case-insensitive counting uses lowercased strings; str.count stays
# non-overlapping and Unicode-aware, matching the case-sensitive semantics
if ignore_case:
fragment_count = contents.lower().count(fragment.lower())
else:
fragment_count = contents.count(fragment)
return fragment_count


Expand All @@ -169,6 +174,7 @@ def specified_entity_greater_than_count( # noqa: PLR0913
containing_directory=constants.markers.Nothing,
contents=constants.markers.Nothing,
exact=False,
ignore_case=False,
):
"""Determine if the entity count is greater than expected."""
# count the fragments/regex in either a file in a directory or String contents
Expand All @@ -178,6 +184,7 @@ def specified_entity_greater_than_count( # noqa: PLR0913
given_file,
containing_directory,
contents,
ignore_case,
)
# check the condition and also return file_entity_count
condition_truth, value = util.greater_than_equal_exacted(
Expand All @@ -188,12 +195,13 @@ def specified_entity_greater_than_count( # noqa: PLR0913
return condition_truth, value, file_entity_count_dictionary


def count_entities(
def count_entities( # noqa: PLR0913
chosen_fragment,
checking_function,
given_file=constants.markers.Nothing,
containing_directory=constants.markers.Nothing,
contents=constants.markers.Nothing,
ignore_case=False,
):
"""Count fragments for the file in the directory (or contents) and a fragment."""
# Use these two variables to keep track of entity counts for multiple files.
Expand All @@ -214,7 +222,12 @@ def count_entities(
if contents is constants.markers.Command_Error:
contents = constants.markers.Nothing
# run the checking_function to look for fragments in the contents
file_contents_count = checking_function(contents, chosen_fragment)
if ignore_case:
file_contents_count = checking_function(
contents, chosen_fragment, ignore_case
)
else:
file_contents_count = checking_function(contents, chosen_fragment)
return file_contents_count, file_contents_count_dictionary
for file_for_checking in files.create_paths(
file=given_file, home=containing_directory
Expand All @@ -227,7 +240,14 @@ def count_entities(
# default character encoding is not UTF-8; this commonly happens on
# Windows systems where the default encoding is usually CP-1252
file_contents = file_for_checking.read_text(encoding="utf-8")
file_contents_count = checking_function(file_contents, chosen_fragment)
if ignore_case:
file_contents_count = checking_function(
file_contents, chosen_fragment, ignore_case
)
else:
file_contents_count = checking_function(
file_contents, chosen_fragment
)
file_contents_count_dictionary[file_for_checking.name] = (
file_contents_count
)
Expand Down
12 changes: 11 additions & 1 deletion gator/invoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
directory=constants.markers.Nothing,
contents=constants.markers.Nothing,
exact=False,
ignore_case=False,
):
"""Perform the check for a fragment existence in file or contents and return the results."""
met_or_exceeded_count = 0
Expand All @@ -497,7 +498,10 @@ def invoke_all_fragment_checks( # noqa: PLR0913
directory,
contents,
exact,
ignore_case,
)
# note the effective matching mode in the message when it differs from the default
mode_suffix = " (case-insensitive)" if ignore_case else ""
# create a message for a file in directory
if (
filecheck is not constants.markers.Nothing
Expand All @@ -515,6 +519,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
+ " of the '"
+ fragment
+ "' fragment"
+ mode_suffix
)
# create an "exact" message, which is an opt-in
else:
Expand All @@ -528,6 +533,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
+ " of the '"
+ fragment
+ "' fragment"
+ mode_suffix
)
# create a message for a string
# this case is run when a program is
Expand All @@ -541,6 +547,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
+ " of the '"
+ fragment
+ "' fragment"
+ mode_suffix
)
# create an "exact" message, which is an opt-in
else:
Expand All @@ -551,6 +558,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
+ " of the '"
+ fragment
+ "' fragment"
+ mode_suffix
)
# produce the diagnostic and report the result
fragment_diagnostic = util.get_file_diagnostic(actual_count_dictionary)
Expand All @@ -570,6 +578,7 @@ def invoke_all_fragment_checks( # noqa: PLR0913
+ "or the output while expecting "
+ ("exactly " if exact else "at least ")
+ str(expected_count)
+ mode_suffix
)
report_result(met_or_exceeded_count, message, diagnostic)
return met_or_exceeded_count
Expand Down Expand Up @@ -679,7 +688,7 @@ def invoke_all_regex_checks( # noqa: PLR0913


def invoke_all_command_fragment_checks(
command, expected_fragment, expected_count, exact=False
command, expected_fragment, expected_count, exact=False, ignore_case=False
):
"""Perform the check for a fragment existence in the output of a command."""
command_output = run.specified_command_get_output(command)
Expand All @@ -698,6 +707,7 @@ def invoke_all_command_fragment_checks(
constants.markers.Nothing,
command_output,
exact,
ignore_case,
)


Expand Down
65 changes: 65 additions & 0 deletions tests/checks/test_check_MatchCommandFragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,46 @@ def test_required_commandline_arguments_can_parse(commandline_arguments, not_rai
_ = check_MatchCommandFragment.parse(commandline_arguments)


@pytest.mark.parametrize(
"commandline_arguments",
[
(
[
"--command",
"run_command_first",
"--fragment",
"fragment",
"--count",
"5",
"--ignore-case",
]
),
],
)
def test_optional_commandline_arguments_can_parse(
commandline_arguments, not_raises
):
"""Check that correct optional command-line arguments check correctly."""
with not_raises(InvalidCheckArgumentsError):
parsed = check_MatchCommandFragment.parse(commandline_arguments)
assert parsed.ignore_case is True


def test_ignore_case_defaults_to_false(not_raises):
"""Check that ignore-case defaults to False when not provided."""
commandline_arguments = [
"--command",
"run_command_first",
"--fragment",
"fragment",
"--count",
"5",
]
with not_raises(InvalidCheckArgumentsError):
parsed = check_MatchCommandFragment.parse(commandline_arguments)
assert parsed.ignore_case is False


@pytest.mark.parametrize(
"commandline_arguments, expected_result",
[
Expand Down Expand Up @@ -195,3 +235,28 @@ def test_act_produces_output(commandline_arguments, expected_result, load_check)
assert report.get_result()["diagnostic"] == ""
else:
assert report.get_result()["diagnostic"] != ""


def test_act_produces_output_ignore_case(load_check):
"""Check that using the check with --ignore-case matches regardless of case."""
testargs = [os.getcwd()]
with patch.object(sys, "argv", testargs):
commandline_arguments = [
"MatchCommandFragment",
"--command",
'echo "TODO: fix this bug"',
"--fragment",
"todo",
"--count",
"1",
"--ignore-case",
]
parsed_arguments, remaining_arguments = arguments.parse(commandline_arguments)
args_verified = arguments.verify(parsed_arguments)
assert args_verified is True
check = load_check(parsed_arguments)
check_result = check.act(parsed_arguments, remaining_arguments)
# the check passes when matching "todo" against "TODO" case-insensitively
assert check_result is True
assert report.get_result() is not None
assert report.get_result()["outcome"] is True
82 changes: 82 additions & 0 deletions tests/checks/test_check_MatchFileFragment.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,50 @@ def test_required_commandline_arguments_can_parse(commandline_arguments, not_rai
_ = check_MatchFileFragment.parse(commandline_arguments)


@pytest.mark.parametrize(
"commandline_arguments",
[
(
[
"--file",
"filename",
"--directory",
"directoryname",
"--count",
"5",
"--fragment",
"fragment",
"--ignore-case",
]
),
],
)
def test_optional_commandline_arguments_can_parse(
commandline_arguments, not_raises
):
"""Check that correct optional command-line arguments check correctly."""
with not_raises(InvalidCheckArgumentsError):
parsed = check_MatchFileFragment.parse(commandline_arguments)
assert parsed.ignore_case is True


def test_ignore_case_defaults_to_false(not_raises):
"""Check that ignore-case defaults to False when not provided."""
commandline_arguments = [
"--file",
"filename",
"--directory",
"directoryname",
"--count",
"5",
"--fragment",
"fragment",
]
with not_raises(InvalidCheckArgumentsError):
parsed = check_MatchFileFragment.parse(commandline_arguments)
assert parsed.ignore_case is False


@pytest.mark.parametrize(
"commandline_arguments, chosen_file, containing_directory, provided_count, expected_result",
[
Expand Down Expand Up @@ -231,3 +275,41 @@ def test_act_produces_output(
assert report.get_result()["diagnostic"] == ""
else:
assert report.get_result()["diagnostic"] != ""


def test_act_produces_output_ignore_case(tmpdir, load_check):
"""Check that using the check with --ignore-case matches regardless of case."""
testargs = [os.getcwd()]
with patch.object(sys, "argv", testargs):
new_file = tmpdir.mkdir("containing_directory").join("file_to_find")
new_file.write("TODO: fix this bug")
assert new_file.read() == "TODO: fix this bug"
assert len(tmpdir.listdir()) == 1
overall_directory = (
tmpdir.dirname
+ "/"
+ tmpdir.basename
+ "/"
+ "containing_directory"
)
commandline_arguments = [
"MatchFileFragment",
"--file",
"file_to_find",
"--directory",
overall_directory,
"--count",
"1",
"--fragment",
"todo",
"--ignore-case",
]
parsed_arguments, remaining_arguments = arguments.parse(commandline_arguments)
args_verified = arguments.verify(parsed_arguments)
assert args_verified is True
check = load_check(parsed_arguments)
check_result = check.act(parsed_arguments, remaining_arguments)
# the check passes when matching "todo" against "TODO" case-insensitively
assert check_result is True
assert report.get_result() is not None
assert report.get_result()["outcome"] is True
Loading