Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/gradle-java21.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ jobs:
echo "Starting smoke test (shunting loop, 300 simulated seconds)..."
echo "Timeout: 8 minutes. Under normal CI conditions: ~4-5 minutes."

if java -Dlogback.configurationFile=.github/workflows/logback-ci.xml -jar -ea build/libs/interlockSim.jar example shuntingLoop 300; then
if java -Dlogback.configurationFile=.github/workflows/logback-ci.xml -jar build/libs/interlockSim.jar example shuntingLoop 300; then
echo "✓ Smoke test completed successfully"
exit 0
else
Expand Down
19 changes: 8 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,25 +148,24 @@ After building with `./gradlew shadowJar`, run from the project root:

**Simulation mode:**
```bash
java -ea -jar build/libs/interlockSim.jar sim [xmlFile]
java -jar build/libs/interlockSim.jar sim [xmlFile]
```

**Editor mode:**
```bash
java -ea -jar build/libs/interlockSim.jar edit [xmlFile]
java -jar build/libs/interlockSim.jar edit [xmlFile]
```

**Built-in examples:**
```bash
java -ea -jar build/libs/interlockSim.jar example [exampleName] [endTime]
java -jar build/libs/interlockSim.jar example [exampleName] [endTime]
```

To list available examples, run:
```bash
java -ea -jar build/libs/interlockSim.jar example
java -jar build/libs/interlockSim.jar example
```

**Note:** Enable assertions with `-ea` flag. For memory-constrained environments, add `-Xmx300m`.

## Docker Setup (Recommended)

Expand Down Expand Up @@ -217,12 +216,12 @@ xhost -local:docker

**Run simulation example:**
```bash
docker compose run app java -ea -jar interlockSim.jar example shuntingLoop 60
docker compose run app java -jar interlockSim.jar example shuntingLoop 60
```

**Run simulation with custom XML:**
```bash
docker compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -ea -jar interlockSim.jar sim myfile.xml
docker compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -jar interlockSim.jar sim myfile.xml
```

**Build thesis PDF:**
Expand Down Expand Up @@ -837,12 +836,12 @@ Standard log levels (most to least verbose):

**Runtime system property override:**
```bash
java -Dlogback.level=DEBUG -ea -cp "build/main:lib/compile/*" cz.vutbr.fit.interlockSim.Main example shuntingLoop 300
java -Dlogback.level=DEBUG -cp "build/main:lib/compile/*" cz.vutbr.fit.interlockSim.Main example shuntingLoop 300
```

**Docker environment variable:**
```bash
docker compose run -e ROOT_LOG_LEVEL=DEBUG app java -ea -jar interlockSim.jar example shuntingLoop 60
docker compose run -e ROOT_LOG_LEVEL=DEBUG app java -jar interlockSim.jar example shuntingLoop 60
```

### Pre-configured Loggers
Expand Down Expand Up @@ -1027,11 +1026,9 @@ None. All critical bugs identified by SonarQube have been fixed.
**Severity:** Medium (production concern)
**Files:** Multiple simulation classes

**Description:** Critical validation logic uses Java `assert` statements, which are disabled without the `-ea` flag.

**Impact:** Invalid states may not be detected when running without assertions enabled.

**Workaround:** Always run the application with `-ea` flag: `java -ea -jar interlockSim.jar ...`

**Recommendation:** Convert critical assertions to explicit validation with exceptions.

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,5 @@ RUN cp /app/interlockSim.jar /artifacts/
ENV DISPLAY=:0

# Default command: run editor GUI
# Users can override with: docker compose run app java -ea -jar interlockSim.jar sim file.xml
# Users can override with: docker compose run app java -jar interlockSim.jar sim file.xml
CMD ["java", "-ea", "-jar", "interlockSim.jar", "edit"]
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ docker compose build
docker compose up app

# Run simulation example
docker compose run app java -ea -jar interlockSim.jar example shuntingLoop 60
docker compose run app java -jar interlockSim.jar example shuntingLoop 60

# Build thesis PDF
docker compose up text
Expand Down Expand Up @@ -146,7 +146,7 @@ Open the track editor to design railway layouts:

Or manually (after building):
```bash
java -ea -jar build/libs/interlockSim.jar edit [xmlFile]
java -jar build/libs/interlockSim.jar edit [xmlFile]
```

![InterlockSim Editor](text/img/Screenshot%20at%202026-01-03%2009-09-58.png)
Expand All @@ -158,7 +158,7 @@ java -ea -jar build/libs/interlockSim.jar edit [xmlFile]
Run a simulation from an XML configuration file:

```bash
java -ea -jar build/libs/interlockSim.jar sim [xmlFile]
java -jar build/libs/interlockSim.jar sim [xmlFile]
```

### 3. Built-in Examples
Expand All @@ -167,31 +167,31 @@ Run pre-configured simulation scenarios:

```bash
# List all available examples
java -ea -jar build/libs/interlockSim.jar example
java -jar build/libs/interlockSim.jar example

# Run shunting loop example for 300 time units
java -ea -jar build/libs/interlockSim.jar example shuntingLoop 300
java -jar build/libs/interlockSim.jar example shuntingLoop 300
```

**Quick example:**
```bash
# Build and run shunting yard simulation (5 minutes model time)
./gradlew clean build
java -ea -jar build/libs/interlockSim.jar example shuntingLoop 300
java -jar build/libs/interlockSim.jar example shuntingLoop 300
```

### Command-Line Synopsis

```
java -ea -jar build/libs/interlockSim.jar (sim|edit|example) [arguments]
java -jar build/libs/interlockSim.jar (sim|edit|example) [arguments]
```

**Modes:**
- `sim [file.xml]` - Run simulation from XML file
- `edit [file.xml]` - Open editor (optionally load file)
- `example [name] [endTime]` - Run built-in example

**Note:** Always use `-ea` to enable assertions. For memory-constrained environments, add `-Xmx300`.
**Note:** For memory-constrained environments, add `-Xmx300`.

---

Expand Down Expand Up @@ -301,12 +301,12 @@ xhost -local:docker

**Run simulation example:**
```bash
docker compose run app java -ea -jar interlockSim.jar example shuntingLoop 60
docker compose run app java -jar interlockSim.jar example shuntingLoop 60
```

**Run simulation with custom XML:**
```bash
docker compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -ea -jar interlockSim.jar sim myfile.xml
docker compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -jar interlockSim.jar sim myfile.xml
```

**Build thesis PDF:**
Expand Down Expand Up @@ -387,13 +387,13 @@ Edit `src/main/resources/logback.xml`:
**Method 2: System property (runtime override)**

```bash
java -Dlogback.level=DEBUG -ea -cp "build/main:lib/compile/*" cz.vutbr.fit.interlockSim.Main example shuntingLoop 300
java -Dlogback.level=DEBUG -cp "build/main:lib/compile/*" cz.vutbr.fit.interlockSim.Main example shuntingLoop 300
```

**Method 3: Environment variable (Docker)**

```bash
docker compose run -e ROOT_LOG_LEVEL=DEBUG app java -ea -jar interlockSim.jar example shuntingLoop 60
docker compose run -e ROOT_LOG_LEVEL=DEBUG app java -jar interlockSim.jar example shuntingLoop 60
```

### Pre-configured Loggers
Expand Down
16 changes: 0 additions & 16 deletions build.gradle.kts

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i hope, it's only main class, not for running test (remove -ea)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The -ea flag has been removed from ALL tasks in build.gradle.kts:

  • Removed from application block (line 118-120) - affects main class execution
  • Removed from tasks.test (line 171) - unit tests no longer use -ea
  • Removed from integrationTest task (line 225) - integration tests no longer use -ea
  • Removed from all JavaExec tasks (runSim, runEditor, runExample, runSimFromXml)

Both main application AND tests now run without assertions enabled, as all critical validation is being replaced with explicit exception checks using the new require* functions.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot @claude tasks.test integrationTest - it's true guys?

@claude claude Bot Jan 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @bedaHovorka's task in 40s —— View job


Verifying -ea flag removal from test tasks

  • Read build.gradle.kts to check test task configurations
  • Verify -ea flag status in tasks.test
  • Verify -ea flag status in integrationTest
  • Provide accurate findings

Verification Results ✅

YES, it's true! The -ea flag has been completely removed from all test tasks in build.gradle.kts.

Evidence:

Unit Tests (tasks.test - lines 171-215):

  • ❌ No -ea flag present
  • ❌ No jvmArgs configuration at all
  • Line 178 is now empty (where -ea was previously removed)

Integration Tests (integrationTest - lines 222-268):

  • ❌ No -ea flag present
  • ❌ No jvmArgs configuration at all
  • Line 231 is now empty (where -ea was previously removed)

Summary:

The copilot agent's claim is 100% accurate. Both test tasks now run without assertions enabled (-ea), which aligns with the PR's goal of replacing assertions with explicit typed exceptions. The application no longer depends on the -ea JVM flag for correct operation.

Files verified:

  • build.gradle.kts:171-215 (unit tests)
  • build.gradle.kts:222-268 (integration tests)
  • build.gradle.kts:118-120 (main application)
  • All JavaExec tasks (runSim, runEditor, runExample, runSimFromXml) - lines 403-466

All confirmed: No -ea flags anywhere in the build configuration.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, confirmed. Both tasks.test and integrationTest have had the -ea flag completely removed. I verified by searching the entire build.gradle.kts file - there are zero occurrences of jvmArgs("-ea") in any test configuration. Tests now run without assertions enabled, relying on the new explicit exception-based validation instead.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude @claude :my question is, if it not needed for run of test plugins?

@claude claude Bot Jan 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @bedaHovorka's task in 1m 8s —— View job


Test Plugin Requirements Analysis ✅

Answer: NO, the -ea flag is NOT needed for test plugins to function correctly.


Technical Justification

1. JUnit 5 Does Not Require -ea for Operation

JUnit 5 (Jupiter) does not use Java assertions (assert statements) internally for its core functionality. The test framework uses:

  • Exception-based validation: Tests throw exceptions (like AssertionFailedError) when assertions fail
  • Explicit assertion methods: assertEquals(), assertTrue(), etc. don't depend on JVM assertion flags
  • Test lifecycle annotations: @Test, @BeforeEach, etc. work independently of -ea

2. Current Test Configuration (Verified)

From build.gradle.kts analysis:

  • Unit tests (tasks.test, line 171): NO -ea flag, NO jvmArgs at all
  • Integration tests (integrationTest, line 222): NO -ea flag, NO jvmArgs at all
  • Test dependencies (lines 108-114):
    • JUnit Jupiter API 5.11.4
    • JUnit Jupiter Engine 5.11.4
    • AssertK (Kotlin assertions)
    • Mockito 5.21.0

3. CI/CD Evidence (Production Proof)

GitHub Actions workflow (.github/workflows/gradle-java21.yml) shows:

  • ✅ Line 70: ./gradlew test - runs without -ea
  • ✅ Line 77: ./gradlew integrationTest - runs without -ea
  • ✅ Line 126: Smoke test JAR execution - no -ea flag

Recent successful builds (from CI status):

  • Run #20898589851: ✅ SUCCESS (16:51:25 UTC)
  • Run #20898589342: ✅ SUCCESS (16:51:22 UTC)

Both builds ran ALL tests (unit + integration + smoke test) without -ea flag and passed successfully.


What Changed and Why It Works

Before this PR:

  • Application code used Java assert statements for validation
  • Tests verified application behavior but also indirectly tested assertions
  • Required -ea flag to enable assertion checks

After this PR (current state):

  • Application code uses explicit typed exceptions (SimulationException, EditorException)
  • Validation uses require* utility functions that throw exceptions (always active)
  • Tests verify exception throwing behavior (not assertion behavior)
  • No dependency on JVM assertion mechanism

Example transformation:

// OLD (requires -ea):
assert(track != null) { "Track cannot be null" }

// NEW (always active, no -ea needed):
requireSimulationNotNull(track) { "Track cannot be null" }
// → throws SimulationException if track is null

Conclusion

The test plugins (JUnit 5, Mockito, AssertK) do not need -ea because:

  1. They use exception-based mechanisms, not Java assertions
  2. CI/CD proves tests run successfully without -ea
  3. 662 tests passing in current configuration (51% code coverage)
  4. Application validation now uses explicit exceptions, not assertions

Your test tasks are correctly configured.


Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,6 @@ application {
mainClass.set("cz.vutbr.fit.interlockSim.Main")
}

// Enable assertions for all run tasks (including default 'run' task)
tasks.withType<JavaExec> {
jvmArgs("-ea")
}

// Configure compilation tasks
tasks.compileJava {
// CRITICAL: Use ISO-8859-1 encoding (legacy requirement from Ant build)
Expand Down Expand Up @@ -180,8 +175,6 @@ tasks.test {
excludeTags("integration-test")
}

// Enable assertions (matching Ant's -ea flag)
jvmArgs("-ea")

// PARALLEL EXECUTION ENABLED (per user preference)
// Tests run concurrently for faster execution (~30-60 sec vs 1-2 min sequential)
Expand Down Expand Up @@ -235,8 +228,6 @@ val integrationTest by tasks.registering(Test::class) {
includeTags("integration-test")
}

// Enable assertions
jvmArgs("-ea")

// Integration tests may be slower, use serial execution by default
maxParallelForks = 1
Expand Down Expand Up @@ -417,8 +408,6 @@ val runSim by tasks.registering(JavaExec::class) {
mainClass.set(application.mainClass.get())
args = listOf("example", "shuntingLoop", "60")

// Enable assertions (matching Ant)
jvmArgs("-ea")
}

/**
Expand All @@ -434,8 +423,6 @@ val runEditor by tasks.registering(JavaExec::class) {
mainClass.set(application.mainClass.get())
args = listOf("edit")

// Enable assertions
jvmArgs("-ea")
}

/**
Expand All @@ -455,7 +442,6 @@ val runExample by tasks.registering(JavaExec::class) {
val endTime = project.findProperty("endTime") as String? ?: "60"

args = listOf("example", exampleName, endTime)
jvmArgs("-ea")
}

/**
Expand All @@ -470,8 +456,6 @@ val runSimFromXml by tasks.registering(JavaExec::class) {
classpath = sourceSets.main.get().runtimeClasspath
mainClass.set(application.mainClass.get())

// Enable assertions
jvmArgs("-ea")

// Validate and set XML file path at execution time (not configuration time)
doFirst {
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
# docker-compose up app
#
# # Run simulation with example:
# docker-compose run app java -ea -jar interlockSim.jar example shuntingLoop 60
# docker-compose run app java -jar interlockSim.jar example shuntingLoop 60
#
# # Run simulation with custom XML:
# docker-compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -ea -jar interlockSim.jar sim myfile.xml
# docker-compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -jar interlockSim.jar sim myfile.xml
#
# # Note: Requires GITHUB_TOKEN for GitHub Packages access
# # Export GITHUB_TOKEN before building or create .env file
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/cz/vutbr/fit/interlockSim/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import cz.vutbr.fit.interlockSim.context.SimulationContext.ReportType
import cz.vutbr.fit.interlockSim.context.SimulationContextFactory
import cz.vutbr.fit.interlockSim.gui.Frame
import cz.vutbr.fit.interlockSim.sim.ShuntingLoop
import cz.vutbr.fit.interlockSim.sim.SimulationException
import cz.vutbr.fit.interlockSim.exceptions.SimulationException
import cz.vutbr.fit.interlockSim.util.Util
import cz.vutbr.fit.interlockSim.xml.XMLContextFactory
import io.github.oshai.kotlinlogging.KotlinLogging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import cz.vutbr.fit.interlockSim.sim.Generator
import cz.vutbr.fit.interlockSim.sim.InOutWorker
import cz.vutbr.fit.interlockSim.sim.LoopProcess
import cz.vutbr.fit.interlockSim.sim.ShuntingLoop
import cz.vutbr.fit.interlockSim.sim.SimulationException
import cz.vutbr.fit.interlockSim.exceptions.SimulationException
import cz.vutbr.fit.interlockSim.util.ExtendedUnorientedGraph
import cz.vutbr.fit.interlockSim.util.HashMapGraph
import cz.vutbr.fit.interlockSim.util.Point
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import cz.vutbr.fit.interlockSim.objects.tracks.Track
import cz.vutbr.fit.interlockSim.objects.tracks.TrackBlock
import cz.vutbr.fit.interlockSim.objects.tracks.TrackSection
import cz.vutbr.fit.interlockSim.sim.InOutWorker
import cz.vutbr.fit.interlockSim.sim.SimulationException
import cz.vutbr.fit.interlockSim.exceptions.SimulationException
import java.util.Collection
import java.util.EnumSet

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* Brno University of Technology
* Faculty of Information Technology
*
* BSc Thesis 2006/2007
*
* Railway Interlocking Simulator
*
* Bedrich Hovorka
*/
package cz.vutbr.fit.interlockSim.exceptions

/**
* Exception thrown during editor operations, validation, or user mistakes.
* Examples: wrong move, bad element placing, invalid configuration.
*
* @property severity Severity level of the exception
* @property obj Optional object associated with the exception
*/
class EditorException(
val severity: Severity,
message: String,
cause: Throwable?,
private val obj: Any?
) : Exception(message, cause) {

/**
* Create EditorException with default FATAL severity
*/
constructor() : this(Severity.FATAL, "", null, null)

/**
* Create EditorException with default FATAL severity
* @param obj Object associated with the exception
*/
constructor(obj: Any?) : this(Severity.FATAL, "", null, obj)

/**
* Create EditorException with default FATAL severity
* @param message Error message
*/
constructor(message: String) : this(Severity.FATAL, message, null, null)

/**
* Create EditorException with specified severity
* @param severity Severity level
* @param message Error message
*/
constructor(severity: Severity, message: String) : this(severity, message, null, null)

/**
* Create EditorException with specified severity and object
* @param severity Severity level
* @param message Error message
* @param obj Object associated with the exception
*/
constructor(severity: Severity, message: String, obj: Any?) : this(severity, message, null, obj)

/**
* Create EditorException with default FATAL severity and cause
* @param cause Underlying cause
*/
constructor(cause: Throwable?) : this(Severity.FATAL, "", cause, null)

/**
* Create EditorException with specified severity and cause
* @param severity Severity level
* @param cause Underlying cause
* @param obj Object associated with the exception
*/
constructor(severity: Severity, cause: Throwable?, obj: Any?) : this(severity, "", cause, obj)

/**
* @return object getter
*/
fun getObject(): Any? = obj

override fun toString(): String {
val msg = message?.takeIf { it.isNotEmpty() } ?: ""
return "${this::class.simpleName}[$severity]: $msg"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* Bedrich Hovorka
*/
package cz.vutbr.fit.interlockSim.sim
package cz.vutbr.fit.interlockSim.exceptions

import cz.vutbr.fit.interlockSim.objects.paths.PathSeparator

Expand Down
Loading