@@ -31,14 +31,17 @@ class TimeoutError(Exception):
3131def handler (signum , frame ):
3232 raise TimeoutError ("Test execution exceeded time limit" )
3333
34+
3435def _runestone_file_id (filename : str , content : str ) -> str :
3536 # Exactly: "runestone" + MD5(fileName + fileContent)
3637 md5 = hashlib .md5 ((filename + content ).encode ("utf-8" )).hexdigest ()
3738 return "runestone" + md5
3839
40+
3941def _b64_text_utf8 (s : str ) -> str :
4042 return base64 .b64encode (s .encode ("utf-8" )).decode ("ascii" )
4143
44+
4245def _jobe_session ():
4346 s = rq .Session ()
4447 s .headers ["Content-type" ] = "application/json; charset=utf-8"
@@ -48,7 +51,9 @@ def _jobe_session():
4851 return s
4952
5053
51- def _ensure_file_on_jobe (sess : rq .Session , base_host : str , file_id : str , content : str ) -> None :
54+ def _ensure_file_on_jobe (
55+ sess : rq .Session , base_host : str , file_id : str , content : str
56+ ) -> None :
5257 """
5358 Mirrors JS logic:
5459 - HEAD /jobeCheckFile/<id>
@@ -64,7 +69,9 @@ def _ensure_file_on_jobe(sess: rq.Session, base_host: str, file_id: str, content
6469 return # already there
6570
6671 if r .status_code not in (404 , 208 ):
67- raise RuntimeError (f"Unexpected HEAD status from JOBE checkFile: { r .status_code } { r .text [:300 ]} " )
72+ raise RuntimeError (
73+ f"Unexpected HEAD status from JOBE checkFile: { r .status_code } { r .text [:300 ]} "
74+ )
6875
6976 put_url = base_host + PUSH_PROXY + file_id
7077 payload = {"file_contents" : _b64_text_utf8 (content )}
@@ -75,12 +82,16 @@ def _ensure_file_on_jobe(sess: rq.Session, base_host: str, file_id: str, content
7582 timeout = 10 ,
7683 )
7784 if pr .status_code != 204 :
78- raise RuntimeError (f"Failed to push file to JOBE: { pr .status_code } { pr .text [:300 ]} " )
85+ raise RuntimeError (
86+ f"Failed to push file to JOBE: { pr .status_code } { pr .text [:300 ]} "
87+ )
88+
7989
8090# Match what the JS client uses
8191PUSH_PROXY = "/ns/rsproxy/jobePushFile/"
8292CHECK_PROXY = "/ns/rsproxy/jobeCheckFile/"
8393
94+
8495def inject_pass_fail_prints (test_code ):
8596 """
8697 Inserts System.out.println("PASS") before System.exit(0)
@@ -96,19 +107,20 @@ def inject_pass_fail_prints(test_code):
96107 test_code = re .sub (
97108 r"(TestHelper\.runAllTests\(\);\s*)(System\.exit\(0\);)" ,
98109 r'\1System.out.println("PASS");\n \2' ,
99- test_code
110+ test_code ,
100111 )
101112
102113 # Insert FAIL prints before System.exit(1) inside catch(Exception e)
103114 if 'System.out.println("FAIL")' not in test_code :
104115 test_code = re .sub (
105116 r"(catch\s*\(\s*Exception\s+e\s*\)\s*\{\s*)(System\.exit\(1\);)" ,
106117 r'\1System.out.println("FAIL");\n System.out.println(e.getMessage());\n \2' ,
107- test_code
118+ test_code ,
108119 )
109120
110121 return test_code
111122
123+
112124# modified from rsproxy.py and livecode.js logic
113125def load_and_run_java_tests (java_code , test_code ):
114126 """
@@ -126,7 +138,7 @@ def extract_class_name(code):
126138 return match .group (1 )
127139 else :
128140 raise ValueError ("Could not find a public class declaration." )
129-
141+
130142 test_code = inject_pass_fail_prints (test_code )
131143 print ("modified_test_code\n " , test_code )
132144 student_class = extract_class_name (java_code )
@@ -135,12 +147,20 @@ def extract_class_name(code):
135147 student_filename = f"{ student_class } .java"
136148 test_filename = f"{ test_class } .java"
137149
138- # Runestone-style file ids: "runestone" + md5(filename + content)
139- student_id = "runestone" + hashlib .md5 ((student_filename + java_code ).encode ("utf-8" )).hexdigest ()
140- test_id = "runestone" + hashlib .md5 ((test_filename + test_code ).encode ("utf-8" )).hexdigest ()
150+ # Runestone-style file ids: "runestone" + md5(filename + content)
151+ student_id = (
152+ "runestone"
153+ + hashlib .md5 ((student_filename + java_code ).encode ("utf-8" )).hexdigest ()
154+ )
155+ test_id = (
156+ "runestone"
157+ + hashlib .md5 ((test_filename + test_code ).encode ("utf-8" )).hexdigest ()
158+ )
141159
142160 runs_url = settings .jobe_server + "/jobe/index.php/restapi/runs/"
143- student_file_url = settings .jobe_server + "/jobe/index.php/restapi/files/" + student_id
161+ student_file_url = (
162+ settings .jobe_server + "/jobe/index.php/restapi/files/" + student_id
163+ )
144164 test_file_url = settings .jobe_server + "/jobe/index.php/restapi/files/" + test_id
145165
146166 sess = rq .Session ()
@@ -157,15 +177,25 @@ def extract_class_name(code):
157177 r = sess .head (student_file_url , timeout = 10 )
158178 if r .status_code != 204 :
159179 # if not found (typically 404), push it
160- put = sess .put (student_file_url , json = {"file_contents" : student_b64 }, timeout = 10 )
180+ put = sess .put (
181+ student_file_url , json = {"file_contents" : student_b64 }, timeout = 10
182+ )
161183 if put .status_code != 204 :
162- return False , {"error" : "Failed to push student file" , "status" : put .status_code , "body" : put .text [:500 ]}
184+ return False , {
185+ "error" : "Failed to push student file" ,
186+ "status" : put .status_code ,
187+ "body" : put .text [:500 ],
188+ }
163189
164190 r = sess .head (test_file_url , timeout = 10 )
165191 if r .status_code != 204 :
166192 put = sess .put (test_file_url , json = {"file_contents" : test_b64 }, timeout = 10 )
167193 if put .status_code != 204 :
168- return False , {"error" : "Failed to push test file" , "status" : put .status_code , "body" : put .text [:500 ]}
194+ return False , {
195+ "error" : "Failed to push test file" ,
196+ "status" : put .status_code ,
197+ "body" : put .text [:500 ],
198+ }
169199
170200 # JOBE runs this, and it calls test class main()
171201 runner_code = f"""public class TestRunner {{
@@ -190,7 +220,11 @@ def extract_class_name(code):
190220 try :
191221 result = resp .json ()
192222 except Exception :
193- return False , {"error" : "Non-JSON JOBE response" , "status" : resp .status_code , "body" : resp .text [:800 ]}
223+ return False , {
224+ "error" : "Non-JSON JOBE response" ,
225+ "status" : resp .status_code ,
226+ "body" : resp .text [:800 ],
227+ }
194228
195229 out = (result .get ("stdout" ) or "" ).strip ()
196230 passed = (result .get ("outcome" ) == 15 ) and out .startswith ("PASS" )
@@ -199,6 +233,7 @@ def extract_class_name(code):
199233 except Exception :
200234 return False
201235
236+
202237def load_and_run_tests (unittest_case , code_to_test , time_limit = 6 ):
203238 """
204239 Load and run Python test cases against the provided code.
0 commit comments