@@ -740,3 +740,237 @@ def separator_reader_worker():
740740 # Always make sure to clean up
741741 stop_event .set ()
742742 setDecimalSeparator (original_separator )
743+
744+
745+ # =============================================================================
746+ # Platform Utils Tests
747+ # =============================================================================
748+
749+
750+ class TestPlatformUtils :
751+ """Tests for mssql_python.platform_utils module."""
752+
753+ def test_get_platform_info_returns_tuple (self ):
754+ """Test that get_platform_info returns a tuple of two strings."""
755+ from mssql_python .platform_utils import get_platform_info
756+
757+ result = get_platform_info ()
758+ assert isinstance (result , tuple )
759+ assert len (result ) == 2
760+ assert isinstance (result [0 ], str ) # architecture
761+ assert isinstance (result [1 ], str ) # platform_tag
762+
763+ def test_get_platform_info_current_platform (self ):
764+ """Test get_platform_info on current platform returns valid values."""
765+ from mssql_python .platform_utils import get_platform_info
766+ import sys
767+
768+ arch , platform_tag = get_platform_info ()
769+
770+ # Architecture should be non-empty
771+ assert arch
772+
773+ # Platform tag should match current platform
774+ if sys .platform .startswith ("win" ):
775+ assert "win" in platform_tag
776+ elif sys .platform .startswith ("darwin" ):
777+ assert "macos" in platform_tag
778+ elif sys .platform .startswith ("linux" ):
779+ assert "linux" in platform_tag
780+
781+ def test_windows_x64_detection (self ):
782+ """Test Windows x64 platform detection."""
783+ from unittest .mock import patch
784+
785+ with patch ("mssql_python.platform_utils.sys" ) as mock_sys , \
786+ patch ("mssql_python.platform_utils.os" ) as mock_os :
787+ mock_sys .platform = "win32"
788+ mock_os .environ .get .return_value = "x64"
789+
790+ from mssql_python import platform_utils
791+ # Force reimport to pick up mocked values
792+ import importlib
793+ importlib .reload (platform_utils )
794+
795+ # Restore for actual test
796+ with patch .object (platform_utils .sys , "platform" , "win32" ):
797+ with patch .object (platform_utils .os .environ , "get" , return_value = "x64" ):
798+ arch , tag = platform_utils .get_platform_info ()
799+ assert arch == "x64"
800+ assert tag == "win_amd64"
801+
802+ def test_windows_x86_detection (self ):
803+ """Test Windows x86 platform detection."""
804+ from unittest .mock import patch
805+ from mssql_python import platform_utils
806+
807+ with patch .object (platform_utils .sys , "platform" , "win32" ):
808+ with patch .object (platform_utils .os .environ , "get" , return_value = "x86" ):
809+ arch , tag = platform_utils .get_platform_info ()
810+ assert arch == "x86"
811+ assert tag == "win32"
812+
813+ def test_windows_arm64_detection (self ):
814+ """Test Windows ARM64 platform detection."""
815+ from unittest .mock import patch
816+ from mssql_python import platform_utils
817+
818+ with patch .object (platform_utils .sys , "platform" , "win32" ):
819+ with patch .object (platform_utils .os .environ , "get" , return_value = "arm64" ):
820+ arch , tag = platform_utils .get_platform_info ()
821+ assert arch == "arm64"
822+ assert tag == "win_arm64"
823+
824+ def test_macos_detection (self ):
825+ """Test macOS platform detection."""
826+ from unittest .mock import patch
827+ from mssql_python import platform_utils
828+
829+ with patch .object (platform_utils .sys , "platform" , "darwin" ):
830+ arch , tag = platform_utils .get_platform_info ()
831+ assert arch == "universal2"
832+ assert "macosx" in tag
833+ assert "universal2" in tag
834+
835+ def test_linux_x86_64_glibc_detection (self ):
836+ """Test Linux x86_64 glibc platform detection."""
837+ from unittest .mock import patch
838+ from mssql_python import platform_utils
839+
840+ with patch .object (platform_utils .sys , "platform" , "linux" ):
841+ with patch .object (platform_utils .os .environ , "get" , return_value = "x86_64" ):
842+ with patch .object (platform_utils .platform , "machine" , return_value = "x86_64" ):
843+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("glibc" , "2.28" )):
844+ arch , tag = platform_utils .get_platform_info ()
845+ assert arch == "x86_64"
846+ assert tag == "manylinux_2_28_x86_64"
847+
848+ def test_linux_x86_64_musl_detection (self ):
849+ """Test Linux x86_64 musl platform detection."""
850+ from unittest .mock import patch
851+ from mssql_python import platform_utils
852+
853+ with patch .object (platform_utils .sys , "platform" , "linux" ):
854+ with patch .object (platform_utils .os .environ , "get" , return_value = "x86_64" ):
855+ with patch .object (platform_utils .platform , "machine" , return_value = "x86_64" ):
856+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("musl" , "1.2" )):
857+ arch , tag = platform_utils .get_platform_info ()
858+ assert arch == "x86_64"
859+ assert tag == "musllinux_1_2_x86_64"
860+
861+ def test_linux_aarch64_glibc_detection (self ):
862+ """Test Linux aarch64 glibc platform detection."""
863+ from unittest .mock import patch
864+ from mssql_python import platform_utils
865+
866+ with patch .object (platform_utils .sys , "platform" , "linux" ):
867+ with patch .object (platform_utils .os .environ , "get" , return_value = "aarch64" ):
868+ with patch .object (platform_utils .platform , "machine" , return_value = "aarch64" ):
869+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("glibc" , "2.28" )):
870+ arch , tag = platform_utils .get_platform_info ()
871+ assert arch == "aarch64"
872+ assert tag == "manylinux_2_28_aarch64"
873+
874+ def test_linux_aarch64_musl_detection (self ):
875+ """Test Linux aarch64 musl platform detection."""
876+ from unittest .mock import patch
877+ from mssql_python import platform_utils
878+
879+ with patch .object (platform_utils .sys , "platform" , "linux" ):
880+ with patch .object (platform_utils .os .environ , "get" , return_value = "aarch64" ):
881+ with patch .object (platform_utils .platform , "machine" , return_value = "aarch64" ):
882+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("musl" , "1.2" )):
883+ arch , tag = platform_utils .get_platform_info ()
884+ assert arch == "aarch64"
885+ assert tag == "musllinux_1_2_aarch64"
886+
887+ def test_linux_arm64_alias (self ):
888+ """Test Linux arm64 is treated as aarch64."""
889+ from unittest .mock import patch
890+ from mssql_python import platform_utils
891+
892+ with patch .object (platform_utils .sys , "platform" , "linux" ):
893+ with patch .object (platform_utils .os .environ , "get" , return_value = "arm64" ):
894+ with patch .object (platform_utils .platform , "machine" , return_value = "arm64" ):
895+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("glibc" , "2.28" )):
896+ arch , tag = platform_utils .get_platform_info ()
897+ assert arch == "aarch64"
898+ assert tag == "manylinux_2_28_aarch64"
899+
900+ def test_linux_empty_libc_with_musl_glob (self ):
901+ """Test Linux with empty libc_ver falls back to glob for musl detection."""
902+ from unittest .mock import patch
903+ from mssql_python import platform_utils
904+
905+ with patch .object (platform_utils .sys , "platform" , "linux" ):
906+ with patch .object (platform_utils .os .environ , "get" , return_value = "x86_64" ):
907+ with patch .object (platform_utils .platform , "machine" , return_value = "x86_64" ):
908+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("" , "" )):
909+ with patch .object (platform_utils .glob , "glob" , return_value = ["/lib/ld-musl-x86_64.so.1" ]):
910+ arch , tag = platform_utils .get_platform_info ()
911+ assert arch == "x86_64"
912+ assert tag == "musllinux_1_2_x86_64"
913+
914+ def test_linux_empty_libc_no_musl_glob (self , capsys ):
915+ """Test Linux with empty libc_ver and no musl glob defaults to glibc."""
916+ from unittest .mock import patch
917+ from mssql_python import platform_utils
918+
919+ with patch .object (platform_utils .sys , "platform" , "linux" ):
920+ with patch .object (platform_utils .os .environ , "get" , return_value = "x86_64" ):
921+ with patch .object (platform_utils .platform , "machine" , return_value = "x86_64" ):
922+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("" , "" )):
923+ with patch .object (platform_utils .glob , "glob" , return_value = []):
924+ arch , tag = platform_utils .get_platform_info ()
925+ assert arch == "x86_64"
926+ assert tag == "manylinux_2_28_x86_64"
927+ # Check warning was printed
928+ captured = capsys .readouterr ()
929+ assert "Warning" in captured .err or "warning" in captured .err .lower ()
930+
931+ def test_linux_unsupported_architecture (self ):
932+ """Test Linux with unsupported architecture raises OSError."""
933+ from unittest .mock import patch
934+ from mssql_python import platform_utils
935+
936+ with patch .object (platform_utils .sys , "platform" , "linux" ):
937+ with patch .object (platform_utils .os .environ , "get" , return_value = "ppc64le" ):
938+ with patch .object (platform_utils .platform , "machine" , return_value = "ppc64le" ):
939+ with patch .object (platform_utils .platform , "libc_ver" , return_value = ("glibc" , "2.28" )):
940+ with pytest .raises (OSError ) as exc_info :
941+ platform_utils .get_platform_info ()
942+ assert "ppc64le" in str (exc_info .value )
943+ assert "Unsupported architecture" in str (exc_info .value )
944+
945+ def test_unsupported_platform (self ):
946+ """Test unsupported platform raises OSError."""
947+ from unittest .mock import patch
948+ from mssql_python import platform_utils
949+
950+ with patch .object (platform_utils .sys , "platform" , "freebsd" ):
951+ with pytest .raises (OSError ) as exc_info :
952+ platform_utils .get_platform_info ()
953+ assert "freebsd" in str (exc_info .value )
954+ assert "Unsupported platform" in str (exc_info .value )
955+
956+ def test_windows_strips_quotes_from_arch (self ):
957+ """Test Windows architecture strips surrounding quotes."""
958+ from unittest .mock import patch
959+ from mssql_python import platform_utils
960+
961+ with patch .object (platform_utils .sys , "platform" , "win32" ):
962+ with patch .object (platform_utils .os .environ , "get" , return_value = '"x64"' ):
963+ arch , tag = platform_utils .get_platform_info ()
964+ assert arch == "x64"
965+ assert tag == "win_amd64"
966+
967+ def test_windows_win32_alias (self ):
968+ """Test Windows win32 is treated as x86."""
969+ from unittest .mock import patch
970+ from mssql_python import platform_utils
971+
972+ with patch .object (platform_utils .sys , "platform" , "win32" ):
973+ with patch .object (platform_utils .os .environ , "get" , return_value = "win32" ):
974+ arch , tag = platform_utils .get_platform_info ()
975+ assert arch == "x86"
976+ assert tag == "win32"
0 commit comments