Skip to content

Commit 0a2b246

Browse files
committed
Enhancing code coverage for the connection and init method
1 parent 0ce4021 commit 0a2b246

File tree

1 file changed

+377
-1
lines changed

1 file changed

+377
-1
lines changed

tests/test_013_SqlHandle_free_shutdown.py

Lines changed: 377 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,372 @@ def test_all_handle_types_comprehensive(self, conn_str):
719719
assert "=== Exiting ===" in result.stdout
720720
print(f"PASS: Comprehensive all handle types test")
721721

722+
def test_cleanup_connections_normal_flow(self, conn_str):
723+
"""
724+
Test _cleanup_connections() with normal active connections.
725+
726+
Validates that:
727+
1. Active connections (_closed=False) are properly closed
728+
2. The cleanup function is registered with atexit
729+
3. Connections can be registered and tracked
730+
"""
731+
script = textwrap.dedent(
732+
f"""
733+
import mssql_python
734+
735+
# Verify cleanup infrastructure exists
736+
assert hasattr(mssql_python, '_active_connections'), "Missing _active_connections"
737+
assert hasattr(mssql_python, '_cleanup_connections'), "Missing _cleanup_connections"
738+
assert hasattr(mssql_python, '_register_connection'), "Missing _register_connection"
739+
740+
# Create mock connection to test registration and cleanup
741+
class MockConnection:
742+
def __init__(self):
743+
self._closed = False
744+
self.close_called = False
745+
746+
def close(self):
747+
self.close_called = True
748+
self._closed = True
749+
750+
# Register connection
751+
mock_conn = MockConnection()
752+
mssql_python._register_connection(mock_conn)
753+
assert mock_conn in mssql_python._active_connections, "Connection not registered"
754+
755+
# Test cleanup
756+
mssql_python._cleanup_connections()
757+
assert mock_conn.close_called, "close() should have been called"
758+
assert mock_conn._closed, "Connection should be marked as closed"
759+
760+
print("Normal flow: PASSED")
761+
"""
762+
)
763+
764+
result = subprocess.run(
765+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
766+
)
767+
768+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
769+
assert "Normal flow: PASSED" in result.stdout
770+
print(f"PASS: Cleanup connections normal flow")
771+
772+
def test_cleanup_connections_already_closed(self, conn_str):
773+
"""
774+
Test _cleanup_connections() with already closed connections.
775+
776+
Validates that connections with _closed=True are skipped
777+
and close() is not called again.
778+
"""
779+
script = textwrap.dedent(
780+
f"""
781+
import mssql_python
782+
783+
class MockConnection:
784+
def __init__(self):
785+
self._closed = True # Already closed
786+
self.close_called = False
787+
788+
def close(self):
789+
self.close_called = True
790+
raise AssertionError("close() should not be called on closed connection")
791+
792+
# Register already-closed connection
793+
mock_conn = MockConnection()
794+
mssql_python._register_connection(mock_conn)
795+
796+
# Cleanup should skip this connection
797+
mssql_python._cleanup_connections()
798+
assert not mock_conn.close_called, "close() should NOT have been called"
799+
800+
print("Already closed: PASSED")
801+
"""
802+
)
803+
804+
result = subprocess.run(
805+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
806+
)
807+
808+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
809+
assert "Already closed: PASSED" in result.stdout
810+
print(f"PASS: Cleanup connections already closed")
811+
812+
def test_cleanup_connections_missing_attribute(self, conn_str):
813+
"""
814+
Test _cleanup_connections() with connections missing _closed attribute.
815+
816+
Validates that hasattr() check prevents AttributeError and
817+
cleanup continues gracefully.
818+
"""
819+
script = textwrap.dedent(
820+
f"""
821+
import mssql_python
822+
823+
class MinimalConnection:
824+
# No _closed attribute
825+
def close(self):
826+
pass
827+
828+
# Register connection without _closed
829+
mock_conn = MinimalConnection()
830+
mssql_python._register_connection(mock_conn)
831+
832+
# Should not crash
833+
mssql_python._cleanup_connections()
834+
835+
print("Missing attribute: PASSED")
836+
"""
837+
)
838+
839+
result = subprocess.run(
840+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
841+
)
842+
843+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
844+
assert "Missing attribute: PASSED" in result.stdout
845+
print(f"PASS: Cleanup connections missing _closed attribute")
846+
847+
def test_cleanup_connections_exception_handling(self, conn_str):
848+
"""
849+
Test _cleanup_connections() exception handling.
850+
851+
Validates that:
852+
1. Exceptions during close() are caught and silently ignored
853+
2. One failing connection doesn't prevent cleanup of others
854+
3. The function completes successfully despite errors
855+
"""
856+
script = textwrap.dedent(
857+
f"""
858+
import mssql_python
859+
860+
class GoodConnection:
861+
def __init__(self):
862+
self._closed = False
863+
self.close_called = False
864+
865+
def close(self):
866+
self.close_called = True
867+
self._closed = True
868+
869+
class BadConnection:
870+
def __init__(self):
871+
self._closed = False
872+
873+
def close(self):
874+
raise RuntimeError("Simulated error during close")
875+
876+
# Register both good and bad connections
877+
good_conn = GoodConnection()
878+
bad_conn = BadConnection()
879+
mssql_python._register_connection(bad_conn)
880+
mssql_python._register_connection(good_conn)
881+
882+
# Cleanup should handle exception and continue
883+
try:
884+
mssql_python._cleanup_connections()
885+
# Should not raise despite bad_conn throwing exception
886+
assert good_conn.close_called, "Good connection should still be closed"
887+
print("Exception handling: PASSED")
888+
except Exception as e:
889+
print(f"Exception handling: FAILED - Exception escaped: {{e}}")
890+
raise
891+
"""
892+
)
893+
894+
result = subprocess.run(
895+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
896+
)
897+
898+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
899+
assert "Exception handling: PASSED" in result.stdout
900+
print(f"PASS: Cleanup connections exception handling")
901+
902+
def test_cleanup_connections_multiple_connections(self, conn_str):
903+
"""
904+
Test _cleanup_connections() with multiple connections.
905+
906+
Validates that all registered connections are processed
907+
and closed in the cleanup iteration.
908+
"""
909+
script = textwrap.dedent(
910+
f"""
911+
import mssql_python
912+
913+
class TestConnection:
914+
count = 0
915+
916+
def __init__(self, conn_id):
917+
self.conn_id = conn_id
918+
self._closed = False
919+
self.close_called = False
920+
921+
def close(self):
922+
self.close_called = True
923+
self._closed = True
924+
TestConnection.count += 1
925+
926+
# Register multiple connections
927+
connections = [TestConnection(i) for i in range(5)]
928+
for conn in connections:
929+
mssql_python._register_connection(conn)
930+
931+
# Cleanup all
932+
mssql_python._cleanup_connections()
933+
934+
assert TestConnection.count == 5, f"All 5 connections should be closed, got {{TestConnection.count}}"
935+
assert all(c.close_called for c in connections), "All connections should have close() called"
936+
937+
print("Multiple connections: PASSED")
938+
"""
939+
)
940+
941+
result = subprocess.run(
942+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
943+
)
944+
945+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
946+
assert "Multiple connections: PASSED" in result.stdout
947+
print(f"PASS: Cleanup connections multiple connections")
948+
949+
def test_cleanup_connections_weakset_behavior(self, conn_str):
950+
"""
951+
Test _cleanup_connections() WeakSet behavior.
952+
953+
Validates that:
954+
1. WeakSet automatically removes garbage collected connections
955+
2. Only live references are processed during cleanup
956+
3. No crashes occur with GC'd connections
957+
"""
958+
script = textwrap.dedent(
959+
f"""
960+
import mssql_python
961+
import gc
962+
963+
class TestConnection:
964+
def __init__(self):
965+
self._closed = False
966+
967+
def close(self):
968+
pass
969+
970+
# Register connection then let it be garbage collected
971+
conn = TestConnection()
972+
mssql_python._register_connection(conn)
973+
initial_count = len(mssql_python._active_connections)
974+
975+
del conn
976+
gc.collect() # Force garbage collection
977+
978+
final_count = len(mssql_python._active_connections)
979+
assert final_count < initial_count, "WeakSet should auto-remove GC'd connections"
980+
981+
# Cleanup should not crash with removed connections
982+
mssql_python._cleanup_connections()
983+
984+
print("WeakSet behavior: PASSED")
985+
"""
986+
)
987+
988+
result = subprocess.run(
989+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
990+
)
991+
992+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
993+
assert "WeakSet behavior: PASSED" in result.stdout
994+
print(f"PASS: Cleanup connections WeakSet behavior")
995+
996+
def test_cleanup_connections_empty_list(self, conn_str):
997+
"""
998+
Test _cleanup_connections() with empty connections list.
999+
1000+
Validates that cleanup completes successfully with no registered
1001+
connections without any errors.
1002+
"""
1003+
script = textwrap.dedent(
1004+
f"""
1005+
import mssql_python
1006+
1007+
# Clear any existing connections
1008+
mssql_python._active_connections.clear()
1009+
1010+
# Should not crash with empty set
1011+
mssql_python._cleanup_connections()
1012+
1013+
print("Empty list: PASSED")
1014+
"""
1015+
)
1016+
1017+
result = subprocess.run(
1018+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
1019+
)
1020+
1021+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
1022+
assert "Empty list: PASSED" in result.stdout
1023+
print(f"PASS: Cleanup connections empty list")
1024+
1025+
def test_cleanup_connections_mixed_scenario(self, conn_str):
1026+
"""
1027+
Test _cleanup_connections() with mixed connection states.
1028+
1029+
Validates handling of:
1030+
- Open connections (should be closed)
1031+
- Already closed connections (should be skipped)
1032+
- Connections that throw exceptions (should be caught)
1033+
- All in one cleanup run
1034+
"""
1035+
script = textwrap.dedent(
1036+
f"""
1037+
import mssql_python
1038+
1039+
class OpenConnection:
1040+
def __init__(self):
1041+
self._closed = False
1042+
self.close_called = False
1043+
1044+
def close(self):
1045+
self.close_called = True
1046+
self._closed = True
1047+
1048+
class ClosedConnection:
1049+
def __init__(self):
1050+
self._closed = True
1051+
1052+
def close(self):
1053+
raise AssertionError("Should not be called")
1054+
1055+
class ErrorConnection:
1056+
def __init__(self):
1057+
self._closed = False
1058+
1059+
def close(self):
1060+
raise RuntimeError("Simulated error")
1061+
1062+
# Register all types
1063+
open_conn = OpenConnection()
1064+
closed_conn = ClosedConnection()
1065+
error_conn = ErrorConnection()
1066+
1067+
mssql_python._register_connection(open_conn)
1068+
mssql_python._register_connection(closed_conn)
1069+
mssql_python._register_connection(error_conn)
1070+
1071+
# Cleanup should handle all scenarios
1072+
mssql_python._cleanup_connections()
1073+
1074+
assert open_conn.close_called, "Open connection should have been closed"
1075+
1076+
print("Mixed scenario: PASSED")
1077+
"""
1078+
)
1079+
1080+
result = subprocess.run(
1081+
[sys.executable, "-c", script], capture_output=True, text=True, timeout=10
1082+
)
1083+
1084+
assert result.returncode == 0, f"Test failed. stderr: {result.stderr}"
1085+
assert "Mixed scenario: PASSED" in result.stdout
1086+
print(f"PASS: Cleanup connections mixed scenario")
1087+
7221088

7231089
if __name__ == "__main__":
7241090
# Allow running test directly for debugging
@@ -754,9 +1120,19 @@ def test_all_handle_types_comprehensive(self, conn_str):
7541120
test.test_gc_during_shutdown_with_circular_refs(conn_str)
7551121
test.test_all_handle_types_comprehensive(conn_str)
7561122

1123+
print("\n--- CLEANUP CONNECTIONS COVERAGE TESTS ---\n")
1124+
test.test_cleanup_connections_normal_flow(conn_str)
1125+
test.test_cleanup_connections_already_closed(conn_str)
1126+
test.test_cleanup_connections_missing_attribute(conn_str)
1127+
test.test_cleanup_connections_exception_handling(conn_str)
1128+
test.test_cleanup_connections_multiple_connections(conn_str)
1129+
test.test_cleanup_connections_weakset_behavior(conn_str)
1130+
test.test_cleanup_connections_empty_list(conn_str)
1131+
test.test_cleanup_connections_mixed_scenario(conn_str)
1132+
7571133
print("\n" + "=" * 70)
7581134
print("PASS: ALL TESTS PASSED - No segfaults detected")
7591135
print("=" * 70 + "\n")
7601136
except AssertionError as e:
761-
print(f"\n TEST FAILED: {e}")
1137+
print(f"\nFAIL: TEST FAILED: {e}")
7621138
sys.exit(1)

0 commit comments

Comments
 (0)