In ideal conditions, the entire unit test suite is expected to complete in just over one minute on Linux and approximately two minutes on Windows.
Unit tests are defined in the TEST_CFGS dictionary using the following structure:
Key: The name of the unit test.
Value: A tuple containing the command-line arguments for the test and the expected console output.
Unit test descriptions are retrieved via print_results and get_detail functions, using tags derived from TEST_CFGS keys plus extended ones for complex standalone tests. Tags correspond to entries in the following localization files:
<HUMBLE_PROJECT_ROOT>/l10n/details.txt
<HUMBLE_PROJECT_ROOT>/l10n/details_es.txt
Classes, Functions and unit tests of test_humble.py.
PythonVersion
Bases: NamedTuple
Python version fields for test_unsupported_python_version.
Source code in tests/test_humble.py
296297298299300
classPythonVersion(NamedTuple):"""Python version fields for `test_unsupported_python_version`."""major:intminor:int
cleanup_analysis_history()
Truncate the test analysis history file.
After all unit tests complete, retaining only the first twenty-five lines to
ensure file size stability while preserving data required for testing.
defcleanup_analysis_history():"""Truncate the test analysis history file. After all unit tests complete, retaining only the first twenty-five lines to ensure file size stability while preserving data required for testing. """original_lines=[]withsuppress(Exception), \
HUMBLE_TEMP_HISTORY.open(encoding="utf-8")ashistory_file:original_lines.extend(next(history_file)for_inrange(25))ifnotoriginal_lines:returnwithsuppress(Exception), \
HUMBLE_TEMP_HISTORY.open("w",encoding="utf-8")asoriginal_file:original_file.writelines(original_lines)original_file.flush()fsync(original_file.fileno())
delete_export_files(extension,ko_msg)
Remove temporary files from export unit tests.
Source code in tests/test_humble.py
541542543544545546547548549550551552553554
defdelete_export_files(extension,ko_msg):"""Remove temporary files from export unit tests."""msgs=[]forexport_fileinHUMBLE_TESTS_DIR.iterdir():name_lower=export_file.name.lower()if(name_lower.startswith(HUMBLE_TEMP_PREFIX)andname_lower.endswith(extension)):try:export_file.unlink()exceptOSErrorascleanup_err:error_detail=get_detail(ko_msg,replace=True)msgs.append((error_detail,f"({type(cleanup_err).__name__}) {export_file}"))returnmsgs
delete_pytest_caches(dir_path)
Remove PYTEST_CACHE_DIRS items following the run of unit tests.
Source code in tests/test_humble.py
557558559560561562563564565566567568
defdelete_pytest_caches(dir_path):"""Remove `PYTEST_CACHE_DIRS` items following the run of unit tests."""msgs=[]path_obj=Path(dir_path)ifpath_obj.is_dir():try:shutil.rmtree(path_obj)exceptOSErrorasrmtree_err:error_detail=get_detail("[test_fcache]",replace=True)msgs.append((error_detail,f"({type(rmtree_err).__name__}) {path_obj}"))returnmsgs
delete_temp_content()
Remove the files and folders after all tests have been run.
Source code in tests/test_humble.py
591592593594595596597598599600601602603604605606
defdelete_temp_content():"""Remove the files and folders after all tests have been run."""now=datetime.now().astimezone()current_time=now.strftime("%Y/%m/%d - %H:%M:%S")info_msgs=set_temp_content(current_time)error_msgs=[(msg,val)formsg,valininfo_msgsifmsg.startswith("Failed")]info_msgs=[(msg,val)formsg,valininfo_msgsifnotmsg.startswith("Failed")]max_msg_len=len(get_detail("[test_tests]",replace=True))formessage,valueinerror_msgs:print(f"[ERROR] {message.ljust(max_msg_len+1)}: {value}")iferror_msgs:print()formessage,valueininfo_msgs:print(f"[INFO] {message.ljust(max_msg_len+1)}: {value}")
delete_temp_coverage()
Set up session globals and clean up temporary files after testing.
Source code in tests/test_humble.py
645646647648649650651652
@pytest.fixture(scope="session",autouse=True)# noqa: vulturedefdelete_temp_coverage():"""Set up session globals and clean up temporary files after testing."""args.lang="en"l10n_main[:]=get_l10n_content()yieldcleanup_analysis_history()delete_temp_content()
get_detail(id_mode,*,replace=False)
Print a message, optionally removing newlines.
Source code in tests/test_humble.py
303304305306307308309
defget_detail(id_mode,*,replace=False):"""Print a message, optionally removing newlines."""fori,lineinenumerate(l10n_main):ifline.startswith(id_mode):return(l10n_main[i+1].replace("\n",""))ifreplaceelse \
l10n_main[i+1]returnNone
get_l10n_content()
Assign the lookup file to handle localized messages and errors.
Source code in tests/test_humble.py
312313314315316317318319320
defget_l10n_content():"""Assign the lookup file to handle localized messages and errors."""ifargs.lang=="en":l10n_file=HUMBLE_L10N_FILE[0]elifargs.lang=="es":l10n_file=HUMBLE_L10N_FILE[1]l10n_path=HUMBLE_TESTS_DIR/HUMBLE_L10N_DIR/l10n_filewithl10n_path.open(encoding="utf-8")asl10n_content:returnl10n_content.readlines()
make_test_func(cfg_key)
Generate unit test execution function.
Skips test_wrong_testssl on Windows due to the Unix-environment
requirement (Cygwin, MSYS2, or Windows Subsystem for Linux) for testssl.sh
Source code in tests/test_humble.py
377378379380381382383384385386387388389390391392
defmake_test_func(cfg_key):"""Generate unit test execution function. Skips `test_wrong_testssl` on Windows due to the Unix-environment requirement (Cygwin, MSYS2, or Windows Subsystem for Linux) for testssl.sh """deftest_func():returnrun_test(*TEST_CFGS[cfg_key])ifcfg_key=="test_wrong_testssl":test_func=pytest.mark.skipif(system().lower()=="windows",reason="'test_wrong_testssl' skipped on Windows",)(test_func)returntest_func
parse_expected_text(output,expected_text)
Validate output against expected results after each test.
Source code in tests/test_humble.py
365366367368369370371372373374
defparse_expected_text(output,expected_text):"""Validate output against expected results after each test."""exp_msg=get_detail("[test_expected]",replace=True)not_found_msg=get_detail("[test_notfound]",replace=True)ifisinstance(expected_text,list|tuple|set):ifall(enotinoutputforeinexpected_text):pytest.fail(f"{exp_msg}{expected_text}{not_found_msg}")returnifexpected_textnotinoutput:pytest.fail(f"{exp_msg} '{expected_text}' {not_found_msg}")
print_results()
Show the description of each unit test.
Descriptions are retrieved via get_detail function using tags derived
from TEST_CFGS keys plus extended ones for complex standalone unit tests.
Tags correspond to entries in the following localization files:
defprint_results():"""Show the description of each unit test. Descriptions are retrieved via `get_detail` function using tags derived from `TEST_CFGS` keys plus extended ones for complex standalone unit tests. Tags correspond to entries in the following localization files: - `<HUMBLE_PROJECT_ROOT>/l10n/details.txt` - `<HUMBLE_PROJECT_ROOT>/l10n/details_es.txt` """print()dynamic_tags=[f"[{key}]"forkeyinTEST_CFGS]all_tags=dynamic_tags+EXTENDED_TAGSdescriptions=[(tag,get_detail(tag,replace=True))fortaginall_tags]max_len=max(len(tag.strip("[]"))fortaginall_tags)fortag,detailindescriptions:label=tag.strip("[]").ljust(max_len+1)print(f"{label}:{detail}")print()
run_test(args,expected_text,timeout=15)
Run unit test and check for expected console output.
defrun_test(args,expected_text,timeout=15):"""Run unit test and check for expected console output."""test_args=[TEST_URLS[9]ifaisNoneelseaforainargs]try:result=subprocess.run([sys.executable,HUMBLE_MAIN_FILE,*test_args],capture_output=True,text=True,timeout=timeout,encoding="utf-8",errors="replace",check=False,)output=result.stdout+result.stderrexceptsubprocess.TimeoutExpired:pytest.fail(get_detail("[test_timeout]",replace=True))parse_expected_text(output,expected_text)
set_temp_content(current_time)
Define the files and folders to be purged upon completion of tests.
defset_temp_content(current_time):"""Define the files and folders to be purged upon completion of tests."""info_msgs=[(get_detail("[test_tests]",replace=True),current_time)]delete_extensions=[(".csv","[test_fcsv]"),(".txt","[test_ftxt]"),(".html","[test_fhtml]"),(".json","[test_fjson]"),(".json","[test_fjson_brief]"),(".pdf","[test_fpdf]"),(".xlsx","[test_fxlsx]"),(".xml","[test_fxml]"),]forextension,ko_msgindelete_extensions:info_msgs.extend(delete_export_files(extension,ko_msg))forcache_dirinPYTEST_CACHE_DIRS:info_msgs.extend(delete_pytest_caches(cache_dir))returninfo_msgs
test_cicd_error(capsys)
Verify an error is displayed in CI/CD results.
Source code in tests/test_humble.py
399400401402403404405406407408409410411412413
deftest_cicd_error(capsys):"""Verify an error is displayed in CI/CD results."""withsuppress(SystemExit):_spec.loader.exec_module(humble_module)humble_module.l10n_main=l10n_mainhumble_module.args=argswith(patch.object(humble_module,"get_cicd_labels",side_effect=Exception),patch.object(humble_module,"get_detail",return_value=ASSERT_STR[1]),):withpytest.raises(SystemExit)aswrapped_exit:humble_module.print_cicd_totals("any_file.tmp")assertwrapped_exit.value.code==1captured=capsys.readouterr()assertASSERT_STR[0]incaptured.out.lower()
test_file_access_errors(capsys)
Verify an error is displayed related to file access.
Test whether the export or history files cannot be accessed or created.
deftest_file_access_errors(capsys):"""Verify an error is displayed related to file access. Test whether the export or history files cannot be accessed or created. """withsuppress(SystemExit):_spec.loader.exec_module(humble_module)humble_module.l10n_main,humble_module.args=l10n_main,argswithpatch("pathlib.Path.open",side_effect=OSError), \
patch.object(humble_module,"delete_lines"):_,res=humble_module.validate_file_access("f.txt",context="history")assertres[0]in("Not available","No disponible")withpatch.object(humble_module,"get_detail",return_value=HUMBLE_TEMP_HISTORY):humble_module.validate_file_access("f.txt",context="basic")out=capsys.readouterr().out.lower()assertstr(HUMBLE_TEMP_HISTORY).lower()inoutwithpatch.object(humble_module,"get_detail",return_value=ASSERT_STR[1]):withpytest.raises(SystemExit)aswrapped_exit:humble_module.validate_file_access("f.txt",context="export")assertwrapped_exit.value.code==1assertASSERT_STR[0]incapsys.readouterr().out.lower()
test_missing_arguments()
Consolidates multiple checks for missing required arguments.
Test multiple missing argument scenarios within a single unit test.
Source code in tests/test_humble.py
517518519520521522523524525526527528529530531
deftest_missing_arguments():"""Consolidates multiple checks for missing required arguments. Test multiple missing argument scenarios within a single unit test. """expected=["Error:","error:","TXT","HTML","Analysis"]run_test(["-H","Cache-Control: no-cache"],expected)run_test(["-if","humble_test.txt","-r"],expected)run_test(["-if","humble_test.txt"],expected)run_test(["-l","es"],expected)run_test(["-of","humble_test.txt"],expected)run_test(["-of","humble_test.html","-o","html","-u",TEST_URLS[9]],expected)run_test(["-b"],expected)run_test(["-s"],expected)
test_outdated_humble(capsys)
Verify an error is displayed related to outdated versions.
Test whether the local version of humble.py is more than 30 days older
than the GitHub version.
deftest_outdated_humble(capsys):"""Verify an error is displayed related to outdated versions. Test whether the local version of `humble.py` is more than 30 days older than the GitHub version. """withsuppress(SystemExit):_spec.loader.exec_module(humble_module)humble_module.l10n_main=l10n_mainhumble_module.args=argsmock_github_version=date(2026,3,6)mock_local_version=date(2026,1,1)mock_days_diff=(mock_github_version-mock_local_version).dayshumble_module.check_updates_diff(mock_days_diff,mock_github_version,mock_local_version)captured=capsys.readouterr()assertmock_github_version.isoformat()incaptured.out
test_proxy_wrong()
Consolidate missing argument checks across proxy-related features.
Source code in tests/test_humble.py
534535536537538
deftest_proxy_wrong():"""Consolidate missing argument checks across proxy-related features."""expected=["Error:","error:"]run_test(["-p","https://"],expected)run_test(["-p","http://127.0.0.1:test"],expected)
test_python_version()
Returns an error message related to Python version.
Test whether the current Python version does not meet the minimum
requirement.
Source code in tests/test_humble.py
472473474475476477478479480481482
deftest_python_version():"""Returns an error message related to Python version. Test whether the current Python version does not meet the minimum requirement. """ifsys.version_info[:2]<REQUIRED_PYTHON_VERSION:pytest.fail(f"{get_detail('[test_pythonm]',replace=True)} "f"{sys.version_info.major}.{sys.version_info.minor}",)
test_testssl_error(capsys)
Verify an error is displayed for TLS/SSL check exceptions.
Source code in tests/test_humble.py
441442443444445446447448449450
deftest_testssl_error(capsys):"""Verify an error is displayed for TLS/SSL check exceptions."""humble_module.l10n_main=l10n_mainhumble_module.args=argswithpatch.object(humble_module,"Popen",side_effect=OSError):withpytest.raises(SystemExit)aswrapped_exit:humble_module.testssl_analysis(TESTSSL_CMD)assertwrapped_exit.value.code==1captured=capsys.readouterr()assertASSERT_STR[0]incaptured.out.lower()
test_unsupported_python_version(capsys)
Verify an error is displayed related to Python version.
Test whether the Python version is below the minimum supported version.
Source code in tests/test_humble.py
485486487488489490491492493494495496497498499500
deftest_unsupported_python_version(capsys):"""Verify an error is displayed related to Python version. Test whether the Python version is below the minimum supported version. """mocked_python_version=PythonVersion(3,10)withsuppress(SystemExit):_spec.loader.exec_module(humble_module)humble_module.l10n_main=l10n_mainhumble_module.args=argswithpatch("sys.version_info",mocked_python_version):withpytest.raises(SystemExit)aswrapped_exit:humble_module.check_python_version()assertwrapped_exit.value.code==1captured=capsys.readouterr()assert"humble"incaptured.out.lower()
test_updates_error(capsys)
Verify an error message is displayed if the GitHub update check fails.
Source code in tests/test_humble.py
503504505506507508509510511512513514
deftest_updates_error(capsys):"""Verify an error message is displayed if the GitHub update check fails."""withsuppress(SystemExit):_spec.loader.exec_module(humble_module)humble_module.l10n_main=l10n_mainhumble_module.args=argswithpatch("requests.get",side_effect=RequestException):withpytest.raises(SystemExit)aswrapped_exit:humble_module.check_updates(date(2026,1,1))assertwrapped_exit.value.code==1captured=capsys.readouterr()assertASSERT_STR[0]incaptured.out.lower()