@@ -773,3 +773,107 @@ def test_program_options_as_bytes_nvvm_unsupported_option():
773773 options = ProgramOptions (arch = "sm_80" , lineinfo = True )
774774 with pytest .raises (CUDAError , match = "not supported by NVVM backend" ):
775775 options .as_bytes ("nvvm" )
776+
777+
778+ def test_program_options_repr ():
779+ """ProgramOptions.__repr__ returns a human-readable string."""
780+ opts = ProgramOptions (name = "mykernel" , arch = "sm_80" )
781+ r = repr (opts )
782+ assert "ProgramOptions" in r
783+ assert "mykernel" in r
784+ assert "sm_80" in r
785+
786+
787+ def test_program_options_bad_define_macro_short_tuple ():
788+ """define_macro with a 1-element tuple raises RuntimeError."""
789+ opts = ProgramOptions (name = "test" , arch = "sm_80" , define_macro = ("ONLY_NAME" ,))
790+ with pytest .raises (RuntimeError , match = "Expected define_macro tuple" ):
791+ opts .as_bytes ("nvrtc" )
792+
793+
794+ def test_program_options_bad_define_macro_non_str_value ():
795+ """define_macro tuple with a non-string value raises RuntimeError."""
796+ opts = ProgramOptions (name = "test" , arch = "sm_80" , define_macro = ("MY_MACRO" , 99 ))
797+ with pytest .raises (RuntimeError , match = "Expected define_macro tuple" ):
798+ opts .as_bytes ("nvrtc" )
799+
800+
801+ def test_program_options_bad_define_macro_list_non_str ():
802+ """define_macro list containing a non-str/non-tuple item raises RuntimeError."""
803+ opts = ProgramOptions (name = "test" , arch = "sm_80" , define_macro = [42 ])
804+ with pytest .raises (RuntimeError , match = "Expected define_macro" ):
805+ opts .as_bytes ("nvrtc" )
806+
807+
808+ def test_program_options_bad_define_macro_list_bad_tuple ():
809+ """define_macro list with a malformed tuple inside raises RuntimeError."""
810+ opts = ProgramOptions (name = "test" , arch = "sm_80" , define_macro = [("ONLY_NAME" ,)])
811+ with pytest .raises (RuntimeError , match = "Expected define_macro" ):
812+ opts .as_bytes ("nvrtc" )
813+
814+
815+ def test_ptx_program_extra_sources_unsupported (ptx_code_object ):
816+ """PTX backend raises ValueError when extra_sources is specified."""
817+ options = ProgramOptions (extra_sources = [("module1" , b"data" )])
818+ with pytest .raises (ValueError , match = "extra_sources is not supported by the PTX backend" ):
819+ Program (ptx_code_object .code .decode (), "ptx" , options )
820+
821+
822+ def test_ptx_program_handle_is_linker_handle (init_cuda , ptx_code_object ):
823+ """Program.handle for the PTX backend delegates to the linker handle."""
824+ program = Program (ptx_code_object .code .decode (), "ptx" )
825+ handle = program .handle
826+ assert handle is not None
827+ assert int (handle ) != 0
828+ program .close ()
829+
830+
831+ @nvvm_available
832+ def test_nvvm_program_wrong_code_type ():
833+ """NVVM backend raises TypeError when code is not str/bytes/bytearray."""
834+ with pytest .raises (TypeError , match = "NVVM IR code must be provided as str, bytes, or bytearray" ):
835+ Program (42 , "nvvm" )
836+
837+
838+ def test_extra_sources_not_sequence ():
839+ """extra_sources must be a sequence; non-sequence raises TypeError."""
840+ with pytest .raises (TypeError , match = "extra_sources must be a sequence of 2-tuples" ):
841+ ProgramOptions (name = "test" , arch = "sm_80" , extra_sources = 42 )
842+
843+
844+ def test_extra_sources_bad_module_not_tuple ():
845+ """extra_sources items must be 2-tuples; non-tuple item raises TypeError."""
846+ with pytest .raises (TypeError , match = "Each extra module must be a 2-tuple" ):
847+ ProgramOptions (name = "test" , arch = "sm_80" , extra_sources = ["not_a_tuple" ])
848+
849+
850+ def test_extra_sources_bad_module_name_not_str ():
851+ """extra_sources module name must be a string; non-str raises TypeError."""
852+ with pytest .raises (TypeError , match = "Module name at index 0 must be a string" ):
853+ ProgramOptions (name = "test" , arch = "sm_80" , extra_sources = [(42 , b"source" )])
854+
855+
856+ def test_extra_sources_bad_module_source_wrong_type ():
857+ """extra_sources module source must be str/bytes/bytearray."""
858+ with pytest .raises (TypeError , match = "Module source at index 0 must be str" ):
859+ ProgramOptions (name = "test" , arch = "sm_80" , extra_sources = [("mod" , 42 )])
860+
861+
862+ def test_extra_sources_empty_source ():
863+ """extra_sources module source cannot be empty bytes."""
864+ with pytest .raises (ValueError , match = "Module source for 'mod'.*cannot be empty" ):
865+ ProgramOptions (name = "test" , arch = "sm_80" , extra_sources = [("mod" , b"" )])
866+
867+
868+ def test_nvrtc_compile_with_logs_capture (init_cuda ):
869+ """Program.compile with logs= exercises the NVRTC program-log reading path."""
870+ import io
871+
872+ # #warning generates a non-empty NVRTC program log, ensuring logsize > 1.
873+ code = '#warning "test log capture"\n extern "C" __global__ void my_kernel() {}'
874+ program = Program (code , "c++" )
875+ logs = io .StringIO ()
876+ result = program .compile ("ptx" , logs = logs )
877+ assert isinstance (result , ObjectCode )
878+ assert logs .getvalue (), "Expected non-empty compilation log from #warning directive"
879+ program .close ()
0 commit comments