@@ -304,7 +304,8 @@ def setUpSolverError(cls):
304304 .with_open_version (local_path = RustBuildTestCase .get_open_local_absolute_path ()) \
305305 .with_build_directory (RustBuildTestCase .TEST_DIR ) \
306306 .with_build_mode (og .config .BuildConfiguration .DEBUG_MODE ) \
307- .with_tcp_interface_config (tcp_interface_config = tcp_config )
307+ .with_tcp_interface_config (tcp_interface_config = tcp_config ) \
308+ .with_build_c_bindings ()
308309 og .builder .OpEnOptimizerBuilder (problem ,
309310 metadata = meta ,
310311 build_configuration = build_config ,
@@ -791,48 +792,168 @@ def test_rust_build_parametric_halfspace(self):
791792 self .assertTrue (sum ([u [i ] * c [i ] for i in range (5 )]) - b <= eps )
792793 self .assertTrue (- sum ([u [i ] * c [i ] for i in range (5 )]) + b <= eps )
793794
795+ @staticmethod
796+ def rebuild_generated_staticlib (optimizer_name ):
797+ optimizer_dir = os .path .join (RustBuildTestCase .TEST_DIR , optimizer_name )
798+ process = subprocess .Popen (
799+ ["cargo" , "build" ],
800+ cwd = optimizer_dir ,
801+ stdout = subprocess .PIPE ,
802+ stderr = subprocess .PIPE ,
803+ )
804+ _stdout , stderr = process .communicate ()
805+ return process .returncode , stderr .decode ()
806+
794807 @staticmethod
795808 def c_bindings_helper (optimizer_name ):
796- p = subprocess .Popen (["/usr/bin/gcc" ,
797- RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/example_optimizer.c" ,
798- "-I" + RustBuildTestCase .TEST_DIR + "/" + optimizer_name ,
799- "-pthread" ,
800- RustBuildTestCase .TEST_DIR + "/" + optimizer_name +
801- "/target/debug/lib" + optimizer_name + ".a" ,
802- "-lm" ,
803- "-ldl" ,
804- "-std=c99" ,
805- "-o" ,
806- RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/optimizer" ],
807- stdout = subprocess .PIPE ,
808- stderr = subprocess .PIPE )
809-
810- # Make sure it compiles
811- p .communicate ()
812- rc1 = p .returncode
813-
814- # Run the optimizer
815- p = subprocess .Popen ([RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/optimizer" ],
816- stdout = subprocess .DEVNULL )
817- p .communicate ()
818- rc2 = p .returncode
819-
820- return rc1 , rc2
809+ compile_process = subprocess .Popen (
810+ ["/usr/bin/gcc" ,
811+ RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/example_optimizer.c" ,
812+ "-I" + RustBuildTestCase .TEST_DIR + "/" + optimizer_name ,
813+ "-pthread" ,
814+ RustBuildTestCase .TEST_DIR + "/" + optimizer_name +
815+ "/target/debug/lib" + optimizer_name + ".a" ,
816+ "-lm" ,
817+ "-ldl" ,
818+ "-std=c99" ,
819+ "-o" ,
820+ RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/optimizer" ],
821+ stdout = subprocess .PIPE ,
822+ stderr = subprocess .PIPE )
823+
824+ compile_stdout , compile_stderr = compile_process .communicate ()
825+
826+ run_stdout = b""
827+ run_stderr = b""
828+ run_returncode = None
829+ if compile_process .returncode == 0 :
830+ run_process = subprocess .Popen (
831+ [RustBuildTestCase .TEST_DIR + "/" + optimizer_name + "/optimizer" ],
832+ stdout = subprocess .PIPE ,
833+ stderr = subprocess .PIPE )
834+ run_stdout , run_stderr = run_process .communicate ()
835+ run_returncode = run_process .returncode
836+
837+ return {
838+ "compile_returncode" : compile_process .returncode ,
839+ "compile_stdout" : compile_stdout .decode (),
840+ "compile_stderr" : compile_stderr .decode (),
841+ "run_returncode" : run_returncode ,
842+ "run_stdout" : run_stdout .decode (),
843+ "run_stderr" : run_stderr .decode (),
844+ }
845+
846+ @staticmethod
847+ def patch_c_bindings_example_parameter_initializer (optimizer_name , replacement_line ):
848+ example_file = os .path .join (
849+ RustBuildTestCase .TEST_DIR , optimizer_name , "example_optimizer.c" )
850+ with open (example_file , "r" , encoding = "utf-8" ) as fh :
851+ example_source = fh .read ()
852+
853+ original_line = None
854+ for line in example_source .splitlines ():
855+ if "double p[" in line and "= {" in line :
856+ original_line = line
857+ break
858+
859+ if original_line is None :
860+ raise RuntimeError ("Could not locate parameter initializer in example_optimizer.c" )
861+
862+ with open (example_file , "w" , encoding = "utf-8" ) as fh :
863+ fh .write (example_source .replace (original_line , replacement_line , 1 ))
864+
865+ return original_line
866+
867+ @staticmethod
868+ def c_bindings_cmake_helper (optimizer_name ):
869+ cmake_executable = shutil .which ("cmake" )
870+ if cmake_executable is None :
871+ raise unittest .SkipTest ("cmake is not available in PATH" )
872+
873+ optimizer_dir = os .path .join (RustBuildTestCase .TEST_DIR , optimizer_name )
874+ build_dir = os .path .join (optimizer_dir , "cmake-build-test" )
875+ if os .path .isdir (build_dir ):
876+ shutil .rmtree (build_dir )
877+ os .makedirs (build_dir )
878+
879+ configure_process = subprocess .Popen (
880+ [cmake_executable , ".." ],
881+ cwd = build_dir ,
882+ stdout = subprocess .PIPE ,
883+ stderr = subprocess .PIPE ,
884+ )
885+ configure_stdout , configure_stderr = configure_process .communicate ()
886+
887+ build_stdout = b""
888+ build_stderr = b""
889+ build_returncode = None
890+ if configure_process .returncode == 0 :
891+ build_process = subprocess .Popen (
892+ [cmake_executable , "--build" , "." ],
893+ cwd = build_dir ,
894+ stdout = subprocess .PIPE ,
895+ stderr = subprocess .PIPE ,
896+ )
897+ build_stdout , build_stderr = build_process .communicate ()
898+ build_returncode = build_process .returncode
899+
900+ return {
901+ "configure_returncode" : configure_process .returncode ,
902+ "configure_stdout" : configure_stdout .decode (),
903+ "configure_stderr" : configure_stderr .decode (),
904+ "build_returncode" : build_returncode ,
905+ "build_stdout" : build_stdout .decode (),
906+ "build_stderr" : build_stderr .decode (),
907+ }
821908
822909 def test_c_bindings (self ):
823- rc1 , rc2 = RustBuildTestCase .c_bindings_helper (
824- optimizer_name = "only_f1" )
825- self .assertEqual (0 , rc1 )
826- self .assertEqual (0 , rc2 )
827-
828- rc1 , rc2 = RustBuildTestCase .c_bindings_helper (
829- optimizer_name = "only_f2" )
830- self .assertEqual (0 , rc1 )
831- self .assertEqual (0 , rc2 )
832-
833- rc1 , rc2 = RustBuildTestCase .c_bindings_helper (optimizer_name = "plain" )
834- self .assertEqual (0 , rc1 )
835- self .assertEqual (0 , rc2 )
910+ result = RustBuildTestCase .c_bindings_helper (optimizer_name = "only_f1" )
911+ self .assertEqual (0 , result ["compile_returncode" ], msg = result ["compile_stderr" ])
912+ self .assertEqual (0 , result ["run_returncode" ], msg = result ["run_stderr" ])
913+ self .assertIn ("Converged" , result ["run_stdout" ])
914+ self .assertIn ("exit status : 0" , result ["run_stdout" ])
915+ self .assertIn ("error code : 0" , result ["run_stdout" ])
916+
917+ result = RustBuildTestCase .c_bindings_helper (optimizer_name = "only_f2" )
918+ self .assertEqual (0 , result ["compile_returncode" ], msg = result ["compile_stderr" ])
919+ self .assertIn ("Converged" , result ["run_stdout" ])
920+ self .assertEqual (0 , result ["run_returncode" ], msg = result ["run_stderr" ])
921+ self .assertIn ("exit status : 0" , result ["run_stdout" ])
922+ self .assertIn ("error code : 0" , result ["run_stdout" ])
923+
924+ result = RustBuildTestCase .c_bindings_helper (optimizer_name = "plain" )
925+ self .assertIn ("Converged" , result ["run_stdout" ])
926+ self .assertEqual (0 , result ["compile_returncode" ], msg = result ["compile_stderr" ])
927+ self .assertEqual (0 , result ["run_returncode" ], msg = result ["run_stderr" ])
928+ self .assertIn ("exit status : 0" , result ["run_stdout" ])
929+ self .assertIn ("error code : 0" , result ["run_stdout" ])
930+
931+ def test_c_bindings_error_path (self ):
932+ rebuild_rc , rebuild_stderr = RustBuildTestCase .rebuild_generated_staticlib (
933+ optimizer_name = "solver_error" )
934+ self .assertEqual (0 , rebuild_rc , msg = rebuild_stderr )
935+
936+ original_line = RustBuildTestCase .patch_c_bindings_example_parameter_initializer (
937+ optimizer_name = "solver_error" ,
938+ replacement_line = " double p[SOLVER_ERROR_NUM_PARAMETERS] = {-1.0};" )
939+ try :
940+ result = RustBuildTestCase .c_bindings_helper (optimizer_name = "solver_error" )
941+ finally :
942+ RustBuildTestCase .patch_c_bindings_example_parameter_initializer (
943+ optimizer_name = "solver_error" ,
944+ replacement_line = original_line )
945+ self .assertEqual (0 , result ["compile_returncode" ], msg = result ["compile_stderr" ])
946+ self .assertNotEqual (0 , result ["run_returncode" ])
947+ self .assertIn ("error code : 2000" , result ["run_stdout" ])
948+ self .assertIn ("forced solver error for TCP test" , result ["run_stdout" ])
949+ self .assertIn (
950+ "Solver returned an error; solution vector is not printed." ,
951+ result ["run_stderr" ])
952+
953+ def test_c_bindings_cmake_example_builds (self ):
954+ result = RustBuildTestCase .c_bindings_cmake_helper (optimizer_name = "plain" )
955+ self .assertEqual (0 , result ["configure_returncode" ], msg = result ["configure_stderr" ])
956+ self .assertEqual (0 , result ["build_returncode" ], msg = result ["build_stderr" ])
836957
837958 def test_tcp_generated_server_builds (self ):
838959 tcp_iface_dir = os .path .join (
0 commit comments