22
33import lombok .Getter ;
44import org .hyperskill .hstest .checker .CheckLibraryVersion ;
5+ import org .hyperskill .hstest .common .ExitCallDetector ;
56import org .hyperskill .hstest .common .FileUtils ;
67import org .hyperskill .hstest .common .ReflectionUtils ;
78import org .hyperskill .hstest .dynamic .ClassSearcher ;
2223import org .junit .runner .JUnitCore ;
2324import org .junit .runner .Result ;
2425
26+ import java .io .IOException ;
2527import java .lang .reflect .Modifier ;
28+ import java .nio .file .Path ;
2629import java .util .ArrayList ;
2730import java .util .List ;
2831import java .util .stream .Collectors ;
@@ -137,6 +140,28 @@ private void printTestNum(int num) {
137140 OutputHandler .print (RED_BOLD + "\n Start test " + num + totalTests + RESET );
138141 }
139142
143+ /**
144+ * Checks user code for forbidden exit calls before running tests
145+ */
146+ private void checkForExitCalls () {
147+ // Only check Java files (other languages handled differently in Docker)
148+ if (!hasJavaSolution (FileUtils .cwd ())) {
149+ return ;
150+ }
151+
152+ try {
153+ Path currentDir = FileUtils .cwd ().toPath ();
154+ ExitCallDetector .DetectionResult result = ExitCallDetector .analyzeDirectory (currentDir );
155+
156+ if (result .hasExitCalls ()) {
157+ throw new WrongAnswer (result .getFormattedMessage ());
158+ }
159+ } catch (IOException e ) {
160+ // If we can't read files, just continue (fail safely)
161+ // The SecurityManager will catch it at runtime if needed
162+ }
163+ }
164+
140165 @ Test
141166 public final void start () {
142167 int currTest = 0 ;
@@ -149,6 +174,9 @@ public final void start() {
149174 ReflectionUtils .setupCwd (this );
150175 }
151176
177+ // Check for exit calls before running any tests
178+ checkForExitCalls ();
179+
152180 List <TestRun > testRuns = initTests ();
153181
154182 for (TestRun testRun : testRuns ) {
0 commit comments