@@ -19,10 +19,13 @@ import assertk.assertions.isNotNull
1919import assertk.assertions.isSameInstanceAs
2020import assertk.assertions.isTrue
2121import cz.vutbr.fit.interlockSim.context.ContextCreationException
22+ import cz.vutbr.fit.interlockSim.context.DefaultContext
2223import cz.vutbr.fit.interlockSim.objects.cells.InOut
2324import cz.vutbr.fit.interlockSim.objects.cells.RailSemaphore
2425import cz.vutbr.fit.interlockSim.objects.cells.RailSwitch
26+ import cz.vutbr.fit.interlockSim.objects.tracks.TrackSection
2527import cz.vutbr.fit.interlockSim.testutil.exists
28+ import cz.vutbr.fit.interlockSim.util.Point
2629import cz.vutbr.fit.interlockSim.testutil.isFile
2730import cz.vutbr.fit.interlockSim.testutil.withMessage
2831import org.junit.jupiter.api.*
@@ -206,6 +209,121 @@ class XMLContextFactoryTest {
206209 assertThat(cellA2).isNotNull().isInstanceOf(InOut ::class )
207210 assertThat(cellB2).isNotNull().isInstanceOf(InOut ::class )
208211 }
212+
213+ @Test
214+ fun parseXML_rudyUjezd_createsValidContext () {
215+ val xml = getFixtureStream(" rudyUjezd.xml" )
216+
217+ val context = factory.createContext(xml)
218+
219+ assertThat(context).isNotNull()
220+ val grid = context.getRailWayNetGrid()
221+ // Check grid size (from rudyUjezd.xml: X=100, Y=100)
222+ assertThat(grid.getCols()).isEqualTo(100 )
223+ assertThat(grid.getRows()).isEqualTo(100 )
224+ // Check presence of at least one InOut, RailSwitch, and RailSemaphore
225+ var hasInOut = false
226+ var hasSwitch = false
227+ var hasSemaphore = false
228+ for (entry in grid) {
229+ when (entry.value) {
230+ is InOut -> hasInOut = true
231+ is RailSwitch -> hasSwitch = true
232+ is RailSemaphore -> hasSemaphore = true
233+ }
234+ }
235+
236+ // in-outs on first end:
237+ // <InOut X="37" Y="32" SpatialType="HORIZONTAL" orientation="true" name="" />
238+ val f1 = context.getRailWayNetGrid().getCellAt(37 , 32 )
239+ // <InOut X="37" Y="31" SpatialType="HORIZONTAL" orientation="true" name="" />
240+ val f2 = context.getRailWayNetGrid().getCellAt(37 , 31 )
241+
242+ // in-outs on second end:
243+ // <InOut X="5" Y="31" SpatialType="HORIZONTAL" orientation="false" name="" />
244+ val s1 = context.getRailWayNetGrid().getCellAt(5 , 31 )
245+ // <InOut X="5" Y="32" SpatialType="HORIZONTAL" orientation="false" name="" />
246+ val s2 = context.getRailWayNetGrid().getCellAt(5 , 32 )
247+
248+ assertThat(f1).isNotNull().isInstanceOf(InOut ::class )
249+ assertThat(f2).isNotNull().isInstanceOf(InOut ::class )
250+ assertThat(s1).isNotNull().isInstanceOf(InOut ::class )
251+ assertThat(s2).isNotNull().isInstanceOf(InOut ::class )
252+
253+ // from each end, there are switches and semaphores leading into the station area and must exist path to each InOut on the other side
254+ assertThat(existPath(f1 as InOut , s1 as InOut , context)).isTrue()
255+ assertThat(existPath(f1, s2 as InOut , context)).isTrue()
256+ assertThat(existPath(f2 as InOut , s1, context)).isTrue()
257+ assertThat(existPath(f2, s2, context)).isTrue()
258+ // and back
259+ assertThat(existPath(s1, f1, context)).isTrue()
260+ assertThat(existPath(s1, f2, context)).isTrue()
261+ assertThat(existPath(s2, f1, context)).isTrue()
262+ assertThat(existPath(s2, f2, context)).isTrue()
263+
264+
265+ assertThat(hasInOut).withMessage(" Should contain at least one InOut" ).isTrue()
266+ assertThat(hasSwitch).withMessage(" Should contain at least one RailSwitch" ).isTrue()
267+ assertThat(hasSemaphore).withMessage(" Should contain at least one RailSemaphore" ).isTrue()
268+ }
269+
270+ /* *
271+ * Checks if a path exists (or can be created) between two InOuts in the railway network.
272+ * Uses BFS to traverse the track graph and verify connectivity.
273+ */
274+ private fun existPath (
275+ from : InOut ,
276+ to : InOut ,
277+ context : DefaultContext
278+ ) : Boolean {
279+ // Get grid locations for both InOuts
280+ val fromLoc = context.getRailWayNetGrid().getLocation(from) ? : return false
281+ val toLoc = context.getRailWayNetGrid().getLocation(to) ? : return false
282+
283+ // If they're the same location, path exists trivially
284+ if (fromLoc == toLoc) return true
285+
286+ // BFS on the track graph
287+ val graph = context.getGraph()
288+ val visited = mutableSetOf<Point >()
289+ val queue = mutableListOf (fromLoc)
290+
291+ while (queue.isNotEmpty()) {
292+ val current = queue.removeFirst()
293+
294+ // Skip if already visited
295+ if (current in visited) continue
296+ visited.add(current)
297+
298+ // Check if we reached the destination
299+ if (current == toLoc) return true
300+
301+ // Get all track blocks connected to this location
302+ val edges = graph.assignedEdges(current)
303+
304+ // For each track block, find the other end and add it to the queue
305+ for (entry in edges.entrySet()) {
306+ val trackBlock = entry.value
307+
308+ // TrackBlocks should be TrackSections which have ends()
309+ if (trackBlock is TrackSection ) {
310+ val ends = trackBlock.ends()
311+ // Get grid locations of both ends
312+ for (pathSeparator in ends) {
313+ val endLocation = context.getRailWayNetGrid().getLocation(pathSeparator)
314+ // Add the other end (not current) to the queue
315+ if (endLocation != null && endLocation != current && endLocation !in visited) {
316+ queue.add(endLocation)
317+ }
318+ }
319+ }
320+ }
321+ }
322+
323+ // No path found
324+ return false
325+ }
326+
209327 }
210328
211329 @Nested
0 commit comments