Skip to content

Commit 7ed71a1

Browse files
CopilotbedaHovorka
andcommitted
Address PR review feedback: fix indentation, resource leak, test naming (#361)
* Address PR review comments: fix indentation, resource leak, test naming, and documentation --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bedaHovorka <5263405+bedaHovorka@users.noreply.github.com>
1 parent 644255c commit 7ed71a1

5 files changed

Lines changed: 86 additions & 84 deletions

File tree

src/jmh/kotlin/cz/vutbr/fit/interlockSim/benchmarks/GridStoragePerformance.kt

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -31,57 +31,57 @@ import kotlin.random.Random
3131
@Fork(value = 2, jvmArgs = ["-ea"])
3232
open class GridStoragePerformance {
3333

34-
// Railway grid dimensions for simulation
35-
private val gridDimension = 1000
36-
37-
// Test data: coordinates to lookup in grid
38-
private lateinit var testCoordinates: List<Point>
39-
40-
// Storage implementations to benchmark
41-
private lateinit var customGridMap: Array2DMap<Int>
42-
private lateinit var standardTreeMap: TreeMap<Point, Int>
34+
// Railway grid dimensions for simulation
35+
private val gridDimension = 1000
4336

44-
@Setup(Level.Trial)
45-
fun prepareTestData() {
46-
// Generate consistent test coordinates using seeded random
47-
val coordinateGenerator = Random(42)
48-
testCoordinates = List(gridDimension) {
49-
Point(
50-
coordinateGenerator.nextInt(gridDimension),
51-
coordinateGenerator.nextInt(gridDimension)
52-
)
53-
}
54-
55-
// Initialize Array2DMap with test data
56-
customGridMap = Array2DMap()
57-
testCoordinates.forEachIndexed { idx, coord ->
58-
customGridMap[coord] = idx
59-
}
60-
61-
// Initialize TreeMap with same data and comparator
62-
standardTreeMap = TreeMap(Array2DMap.POINT_COMPARATOR)
63-
testCoordinates.forEachIndexed { idx, coord ->
64-
standardTreeMap[coord] = idx
65-
}
66-
}
37+
// Test data: coordinates to lookup in grid
38+
private lateinit var testCoordinates: List<Point>
6739

68-
@Benchmark
69-
fun measureCustomGridLookup(): Int {
70-
// Test Array2DMap performance for railway grid cell access
71-
var checksum = 0
72-
for (coord in testCoordinates) {
73-
checksum += customGridMap[coord] ?: 0
74-
}
75-
return checksum
76-
}
40+
// Storage implementations to benchmark
41+
private lateinit var customGridMap: Array2DMap<Int>
42+
private lateinit var standardTreeMap: TreeMap<Point, Int>
7743

78-
@Benchmark
79-
fun measureStandardTreeLookup(): Int {
80-
// Baseline: TreeMap performance for comparison
81-
var checksum = 0
82-
for (coord in testCoordinates) {
83-
checksum += standardTreeMap[coord] ?: 0
84-
}
85-
return checksum
86-
}
44+
@Setup(Level.Trial)
45+
fun prepareTestData() {
46+
// Generate consistent test coordinates using seeded random
47+
val coordinateGenerator = Random(42)
48+
testCoordinates = List(gridDimension) {
49+
Point(
50+
coordinateGenerator.nextInt(gridDimension),
51+
coordinateGenerator.nextInt(gridDimension)
52+
)
53+
}
54+
55+
// Initialize Array2DMap with test data
56+
customGridMap = Array2DMap()
57+
testCoordinates.forEachIndexed { idx, coord ->
58+
customGridMap[coord] = idx
59+
}
60+
61+
// Initialize TreeMap with same data and comparator
62+
standardTreeMap = TreeMap(Array2DMap.POINT_COMPARATOR)
63+
testCoordinates.forEachIndexed { idx, coord ->
64+
standardTreeMap[coord] = idx
65+
}
66+
}
67+
68+
@Benchmark
69+
fun measureCustomGridLookup(): Int {
70+
// Test Array2DMap performance for railway grid cell access
71+
var checksum = 0
72+
for (coord in testCoordinates) {
73+
checksum += customGridMap[coord] ?: 0
74+
}
75+
return checksum
76+
}
77+
78+
@Benchmark
79+
fun measureStandardTreeLookup(): Int {
80+
// Baseline: TreeMap performance for comparison
81+
var checksum = 0
82+
for (coord in testCoordinates) {
83+
checksum += standardTreeMap[coord] ?: 0
84+
}
85+
return checksum
86+
}
8787
}

src/main/kotlin/cz/vutbr/fit/interlockSim/gui/MenuBar.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ class MenuBar : JMenuBar() {
8787
frame.setContext(context)
8888
frame.modificationTracker.setCurrentFile(selectedFile)
8989
frame.modificationTracker.markClean()
90+
} else {
91+
// User cancelled - close context to avoid resource leak
92+
context.close()
9093
}
91-
// If CANCEL, do nothing (file remains closed)
9294
}
9395

9496
// Case 3: Unparseable XML (malformed syntax) - show error and block

src/test/kotlin/cz/vutbr/fit/interlockSim/sim/TrainReverseDirectionTest.kt

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,47 +27,55 @@ import org.koin.test.inject
2727
private val logger = KotlinLogging.logger {}
2828

2929
/**
30-
* Integration tests for Train.reverseDirection() functionality.
30+
* Unit tests for Timetable.reverseDirection() functionality.
3131
*
3232
* ## Purpose
3333
*
34-
* These tests verify that trains can reverse their direction of travel during simulation,
35-
* simulating the train engineer moving to the opposite end of the train.
34+
* These tests verify the timetable reversal logic that supports bidirectional
35+
* train operation. The reversal swaps the In/Out destinations and is used by
36+
* Train.reverseDirection().
3637
*
3738
* ## Test Scenarios
3839
*
3940
* Uses vyhybna.xml network configuration:
40-
* - Train reverses direction when stopped
41-
* - Validates preconditions (train must be stopped)
42-
* - Verifies In/Out destinations are swapped
43-
* - Tests that reversed train can complete journey
41+
* - Timetable reverses In/Out destinations
42+
* - Validates destination swap is correct
43+
* - Verifies other timetable properties remain unchanged
44+
* - Tests that reversed timetable is usable for train creation
45+
*
46+
* ## Note on Train.reverseDirection()
47+
*
48+
* Train.reverseDirection() includes additional simulation behavior (hold(30.0)
49+
* to simulate engineer movement) that requires running simulation. That full
50+
* integration test scenario requires stopping a train mid-simulation in jDisco,
51+
* which is complex to test. This test focuses on the core timetable reversal
52+
* logic that Train.reverseDirection() delegates to.
4453
*
4554
* ## Conservative Approach
4655
*
4756
* Per CLAUDE.md guidance for sim/ package:
4857
* - Uses existing vyhybna.xml network (realistic topology)
49-
* - Short simulation times (30-60 seconds)
50-
* - Validates train state without modifying Train class internals
51-
* - Tests observe behavior through public APIs only
58+
* - Tests core logic without running full simulation
59+
* - Validates state through public APIs only
5260
*
5361
* ## Railway Context
5462
*
5563
* This feature simulates a simplified version of locomotive coupling/uncoupling:
5664
* - In reality, only possible with certain modern train types
5765
* - Simulation allows this for any train for flexibility
58-
* - Engineer movement delay (30s) provides realistic timing
66+
* - Engineer movement delay (30s) in Train.reverseDirection() provides realistic timing
5967
*
6068
* @since 2026-02-06 (GitHub #62)
6169
*/
6270
@Tag("integration-test")
63-
@DisplayName("Train Reverse Direction - Integration Tests")
71+
@DisplayName("Timetable Reverse Direction - Unit Tests")
6472
class TrainReverseDirectionTest : KoinTestBase() {
6573
private val simulationContextFactory: SimulationContextFactory by inject()
6674
private lateinit var context: DefaultSimulationContext
6775

6876
@BeforeEach
6977
fun setUp() {
70-
logger.info { "Loading vyhybna.xml for train reverse direction testing" }
78+
logger.info { "Loading vyhybna.xml for timetable reverse direction testing" }
7179

7280
// Load vyhybna.xml - realistic railway network
7381
val xml =
@@ -78,20 +86,19 @@ class TrainReverseDirectionTest : KoinTestBase() {
7886

7987
context = simulationContextFactory.createContext(xml) as DefaultSimulationContext
8088

81-
logger.info { "Train reverse direction test setup complete" }
89+
logger.info { "Timetable reverse direction test setup complete" }
8290
}
8391

8492
@AfterEach
8593
fun tearDown() {
86-
// Only stop if simulation was actually started
87-
// These tests don't run the simulation, just test the API
94+
// These tests don't run the simulation, just test the timetable API
8895
if (::context.isInitialized) {
89-
logger.info { "Train reverse direction test teardown complete" }
96+
logger.info { "Timetable reverse direction test teardown complete" }
9097
}
9198
}
9299

93100
@Test
94-
fun `train can reverse direction when stopped`() {
101+
fun `timetable can reverse In and Out destinations`() {
95102
// Arrange
96103
val inOuts = context.getInOuts().toList()
97104
assertThat(inOuts.size >= 2).isEqualTo(true)
@@ -114,20 +121,13 @@ class TrainReverseDirectionTest : KoinTestBase() {
114121
assertThat(timetable.getIn()).isEqualTo(inPoint)
115122
assertThat(timetable.getOut()).isEqualTo(outPoint)
116123

117-
// Act - Activate train but don't start simulation yet
118-
// We'll test the reverseDirection method directly
119-
// This is a unit-style test within an integration test framework
120-
121-
// Since we can't easily stop a train mid-simulation in jDisco without
122-
// running the full simulation, we'll test the method when train is in
123-
// initial stopped state (velocity = 0)
124+
// Act - Test the timetable reversal logic directly
125+
// Note: Train.reverseDirection() calls this same method, but also includes
126+
// hold(30.0) simulation delay which requires running simulation context
124127
assertThat(train.getVelocity()).isEqualTo(0.0)
125-
126-
// Reverse direction (this will use hold() internally, so we need to activate as Process)
127-
// For this test, we'll verify the timetable swap happened correctly
128128
timetable.reverseDirection()
129129

130-
// Assert
130+
// Assert - Verify In/Out swap
131131
assertThat(timetable.getIn()).isEqualTo(outPoint)
132132
assertThat(timetable.getOut()).isEqualTo(inPoint)
133133
}

src/test/kotlin/cz/vutbr/fit/interlockSim/xml/XMLContextFactoryLenientTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ class XMLContextFactoryLenientTest : KoinTestBase() {
6262
@DisplayName("Lenient parsing - Parseable XML with validation errors")
6363
inner class ParseableWithErrorsTests {
6464
@Test
65-
fun singleInOut_shouldReturnParseableWithErrors() {
66-
// Arrange: single-inout.xml has only 1 InOut (violates minimum 2 requirement)
65+
fun singleInOut_shouldBeValidAfterBidirectionalSupport() {
66+
// Arrange: single-inout.xml has only 1 InOut (meets minimum 1 requirement)
6767
val fixtureFile = getFixtureFile("single-inout.xml")
6868

6969
// Act
7070
val result = xmlContextFactory.createContextLenient(fixtureFile)
7171

72-
// Assert: Should be parseable but have validation errors
72+
// Assert: Should be parseable and valid (minimum 1 InOut is now allowed)
7373
assertThat(result.isParseable).isTrue()
7474
assertThat(result.context).isNotNull()
7575
assertThat(result.validationResult.isValid).isTrue()

src/test/kotlin/cz/vutbr/fit/interlockSim/xml/XMLPolishTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ class XMLPolishTest : KoinTestBase() {
416416
}
417417

418418
@Test
419-
fun `exactly one InOuts is valid`() {
419+
fun `exactly one InOut is valid`() {
420420
val xml = """<?xml version="1.0"?>
421421
<!DOCTYPE net>
422422
<net X="100" Y="100">

0 commit comments

Comments
 (0)