Files
database/tests/regex.py
Sam Chau d5090d1a9f Feature/Automated Testing (#1) (#2)
* addition: custom formats imported from dev

* feat. Added scripts to conduct automate testing

* feat. Added CODEOWNERS file for reviews

* feat. Added workflow for automated testing on PR

* change. test script now exits on fail

* fix. Use py3 for running regex script

* fix. Adjusted sys exits on failed tests

* addition. Added placeholder json files from profilarr
2024-06-01 23:46:35 +09:30

152 lines
6.3 KiB
Python
Executable File

import json
import subprocess
import time
import re
import os
import sys
from colorama import Fore, Style
class RegexTester:
def __init__(self, max_retries=3, retry_delay=1):
self.max_retries = max_retries
self.retry_delay = retry_delay
def get_latest_revision(self, unique_id):
url = f"https://regex101.com/api/regex/{unique_id}"
retries = 0
while retries < self.max_retries:
try:
response = subprocess.check_output(["curl", "-s", url])
data = json.loads(response.decode("utf-8"))
revisions = data["versions"]
latest_revision = revisions[-1]
return latest_revision
except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
print(f"{Fore.YELLOW}Warning: {str(e)}. Retrying in {self.retry_delay} second(s)...{Style.RESET_ALL}")
retries += 1
time.sleep(self.retry_delay)
raise Exception("Failed to retrieve latest revision after multiple retries.")
def get_regex_data(self, unique_id, revision):
url = f"https://regex101.com/api/regex/{unique_id}/{revision}"
retries = 0
while retries < self.max_retries:
try:
response = subprocess.check_output(["curl", "-s", url])
data = json.loads(response.decode("utf-8"))
regex_pattern = data["regex"]
unit_tests = data["unitTests"]
flavor = data["flavor"]
return regex_pattern, unit_tests, flavor
except (subprocess.CalledProcessError, json.JSONDecodeError) as e:
print(f"{Fore.YELLOW}Warning: {str(e)}. Retrying in {self.retry_delay} second(s)...{Style.RESET_ALL}")
retries += 1
time.sleep(self.retry_delay)
raise Exception("Failed to retrieve regex data after multiple retries.")
def run_unit_tests(self, regex_pattern, flavor, unit_tests):
success_count = 0
failure_count = 0
if flavor == "pcre2":
regex_flags = re.MULTILINE | re.IGNORECASE | re.DOTALL
else:
regex_flags = re.MULTILINE | re.IGNORECASE
for test in unit_tests:
test_string = test["testString"]
criteria = test["criteria"]
target = test["target"]
if target == "REGEX":
matches = re.finditer(regex_pattern, test_string, regex_flags)
match_found = bool(list(matches))
if criteria == "DOES_MATCH":
if match_found:
success_count += 1
print(f"{Fore.GREEN}Passed test{Style.RESET_ALL}: '{test_string}'")
else:
failure_count += 1
print(f"{Fore.RED}Should match{Style.RESET_ALL} '{test_string}'")
elif criteria == "DOES_NOT_MATCH":
if not match_found:
success_count += 1
print(f"{Fore.GREEN}Passed test{Style.RESET_ALL}: '{test_string}'")
else:
failure_count += 1
print(f"{Fore.RED}Shouldn't match{Style.RESET_ALL} '{test_string}'")
return success_count, failure_count
def extract_regex_ids(data):
regex_ids = {}
def extract_ids(obj):
if isinstance(obj, dict):
if "fields" in obj:
for field in obj["fields"]:
if "regexID" in field:
regex_id = field["regexID"]
name = obj.get("name", "")
regex_ids[regex_id] = name
for value in obj.values():
extract_ids(value)
elif isinstance(obj, list):
for item in obj:
extract_ids(item)
extract_ids(data)
return regex_ids
def main():
tester = RegexTester()
regex_dir = "./db/custom_formats"
tested_ids = set()
any_test_failed = False
for root, dirs, files in os.walk(regex_dir):
for filename in files:
if filename.endswith(".json"):
filepath = os.path.join(root, filename)
with open(filepath, "r") as f:
data = json.load(f)
regex_ids = extract_regex_ids(data)
for regex_id, name in regex_ids.items():
if regex_id not in tested_ids:
tested_ids.add(regex_id)
try:
latest_revision = tester.get_latest_revision(regex_id)
regex_pattern, unit_tests, flavor = tester.get_regex_data(regex_id, latest_revision)
print(f"====================")
print(f"Running Test: {Fore.YELLOW}{name}{Style.RESET_ALL}")
print(f"Regex: {Fore.YELLOW}{regex_pattern}{Style.RESET_ALL}")
print(f"Link: {Fore.BLUE}https://regex101.com/r/{regex_id}{Style.RESET_ALL}")
print(f"====================")
print()
total_tests = len(unit_tests)
passed_tests, failed_tests = tester.run_unit_tests(regex_pattern, flavor, unit_tests)
score_color = Fore.GREEN if passed_tests == total_tests else Fore.RED
status_text = "PASS" if failed_tests == 0 else "FAIL"
status_color = Fore.GREEN if status_text == "PASS" else Fore.RED
print()
print(f"====================")
print(f"Score: {score_color}{passed_tests} / {total_tests}{Style.RESET_ALL}")
print(f"Status: {status_color}{status_text}{Style.RESET_ALL}")
print(f"====================")
print()
if failed_tests > 0:
sys.exit(1)
except Exception as e:
print(f"{Fore.RED}Error processing regex {regex_id}: {str(e)}{Style.RESET_ALL}")
print()
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()