test_routing.jl 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. julia_test_file = "test/traces/julia.csv"
  2. @testset "Unconnected Output Port" begin
  3. top = CoupledDEVS(:top)
  4. # Sender produces output, but it is NEVER connected to anything!
  5. sender_ref = add_submodel!(top, :sender, MockNode;
  6. out_ports=(; out=Int), ta=1.0, make_out=true
  7. )
  8. # Compile and run
  9. model = compile_devs(top; logging=julia_test_file)
  10. simulate_devs!(model, 2.0)
  11. test_trace_equivalence(julia_test_file, "test/traces/UnconnectedPort.csv")
  12. sender = model[model.path_to_id["top.sender"]]
  13. # Prove the sender successfully generated the output and transitioned anyway
  14. @test sender.n_outputs == 1
  15. @test sender.n_internal == 1
  16. end
  17. @testset "Pass-Through Test" begin
  18. top = CoupledDEVS(:Top)
  19. sender_ref = add_submodel!(top, :Sender, MockNode;
  20. out_ports=(; out=Int),
  21. make_out=true, ta=1.0
  22. )
  23. forwarder_ref = add_submodel!(top, :Forwarder, MockNode;
  24. inbox=(; in=Int[]),
  25. out_ports=(; out=Int),
  26. forward=true, ta=1.0
  27. )
  28. receiver_ref = add_submodel!(top, :Receiver, MockNode;
  29. inbox=(; in=Int[]),
  30. )
  31. connect!(top, sender_ref.out, forwarder_ref.in)
  32. connect!(top, forwarder_ref.out, receiver_ref.in)
  33. model = compile_devs(top; logging=julia_test_file)
  34. simulate_devs!(model, 2.0)
  35. test_trace_equivalence(julia_test_file, "test/traces/PassThrough.csv")
  36. forwarder = model[model.path_to_id["Top.Forwarder"]]
  37. @test forwarder.n_external == 1
  38. @test length(forwarder.inbox_log) == 1
  39. @test typeof(forwarder.inbox_log[:in][1]) == Int
  40. @test forwarder.inbox_log[:in][1] == 42
  41. @test forwarder.n_internal == 1
  42. @test forwarder.forward_msg === nothing
  43. receiver = model[model.path_to_id["Top.Receiver"]]
  44. @test receiver.n_external == 1
  45. @test length(receiver.inbox_log) == 1
  46. @test typeof(receiver.inbox_log[:in][1]) == Int
  47. @test receiver.inbox_log[:in][1] == 42
  48. end
  49. @testset "Cyclic Couplings" begin
  50. top = CoupledDEVS(:Top)
  51. nodeA_ref = add_submodel!(top, :NodeA, MockNode;
  52. inbox=(; in=Int[]),
  53. out_ports=(; out=Int),
  54. forward=true, ta=1.0, forward_msg=1
  55. )
  56. nodeB_ref = add_submodel!(top, :NodeB, MockNode;
  57. inbox=(; in=Int[]),
  58. out_ports=(; out=Int),
  59. forward=true, ta=1.0
  60. )
  61. connect!(top, nodeA_ref.out, nodeB_ref.in)
  62. connect!(top, nodeB_ref.out, nodeA_ref.in)
  63. model = compile_devs(top; logging=julia_test_file)
  64. simulate_devs!(model, 12.0)
  65. test_trace_equivalence(julia_test_file, "test/traces/CyclicCoupling.csv")
  66. nodeA = model[model.path_to_id["Top.NodeA"]]
  67. @test nodeA.n_internal == 6
  68. @test nodeA.n_external == 6
  69. nodeB = model[model.path_to_id["Top.NodeB"]]
  70. @test nodeB.n_external == 6
  71. end
  72. @testset "Fan-In Routing Test (Simultaneous Arrival)" begin
  73. top = CoupledDEVS(:Top)
  74. sender_ref1 = add_submodel!(top, :Sender1, MockNode;
  75. out_ports=(; out=Int),
  76. ta=1.0, make_out=true
  77. )
  78. sender_ref2 = add_submodel!(top, :Sender2, MockNode;
  79. out_ports=(; out=Int),
  80. ta=1.0, make_out=true
  81. )
  82. receiver_ref = add_submodel!(top, :Receiver, MockNode;
  83. inbox=(; in=Int[]),
  84. )
  85. connect!(top, sender_ref1.out, receiver_ref.in)
  86. connect!(top, sender_ref2.out, receiver_ref.in)
  87. model = compile_devs(top; logging=julia_test_file)
  88. simulate_devs!(model, 2.0)
  89. test_trace_equivalence(julia_test_file, "test/traces/FanIn.csv")
  90. receiver = model[model.path_to_id["Top.Receiver"]]
  91. @test receiver.n_internal == 0
  92. @test receiver.n_external == 1
  93. @test receiver.n_confluent == 0
  94. @test length(receiver.inbox_log[:in]) == 2
  95. end
  96. @testset "Fan-Out Routing Test" begin
  97. top = CoupledDEVS(:Top)
  98. sender_ref = add_submodel!(top, :Sender, MockNode;
  99. out_ports=(; out=Int),
  100. make_out=true, ta=1.0
  101. )
  102. receiver_ref1 = add_submodel!(top, :Receiver1, MockNode;
  103. inbox=(; in=Int[]),
  104. )
  105. receiver_ref2 = add_submodel!(top, :Receiver2, MockNode;
  106. inbox=(; in=Int[]),
  107. )
  108. connect!(top, sender_ref.out, receiver_ref1.in)
  109. connect!(top, sender_ref.out, receiver_ref2.in)
  110. model = compile_devs(top; logging=julia_test_file)
  111. simulate_devs!(model)
  112. test_trace_equivalence(julia_test_file, "test/traces/FanOut.csv")
  113. receiver1 = model[model.path_to_id["Top.Receiver1"]]
  114. receiver2 = model[model.path_to_id["Top.Receiver2"]]
  115. @test receiver1.n_external == 1
  116. @test receiver2.n_external == 1
  117. @test length(receiver1.inbox_log) == 1
  118. @test length(receiver2.inbox_log) == 1
  119. end
  120. @testset "Flattened Connection Routing" begin
  121. parent = CoupledDEVS(:parent)
  122. child1 = CoupledDEVS(:child1)
  123. add_out_port!(child1, :out)
  124. child2 = CoupledDEVS(:child2)
  125. add_in_port!(child2, :in)
  126. sender_ref = add_submodel!(child1, :sender, MockNode;
  127. out_ports=(; out=Int),
  128. make_out=true, ta=1.0
  129. )
  130. receiver_ref = add_submodel!(child2, :receiver, MockNode;
  131. inbox=(; in=Int[]),
  132. )
  133. connect!(child1, sender_ref.out, self.out)
  134. connect!(child2, self.in, receiver_ref.in)
  135. node_ref1 = add_submodel!(parent, :child1, child1)
  136. node_ref2 = add_submodel!(parent, :child2, child2)
  137. connect!(parent, node_ref1.out, node_ref2.in)
  138. model = compile_devs(parent; logging=julia_test_file)
  139. simulate_devs!(model)
  140. test_trace_equivalence(julia_test_file, "test/traces/FlattenedConnection.csv")
  141. receiver = model[model.path_to_id["parent.child2.receiver"]]
  142. @test receiver.n_external == 1
  143. @test length(receiver.inbox_log) == 1
  144. @test typeof(receiver.inbox_log[:in][1]) == Int
  145. @test receiver.inbox_log[:in][1] == 42
  146. end
  147. @testset "Complex Bag Semantics (Multi-Port)" begin
  148. top = CoupledDEVS(:top)
  149. sA = add_submodel!(top, :sA, MockNode; out_ports=(; out=Int), ta=2.0, make_out=true)
  150. sB = add_submodel!(top, :sB, MockNode; out_ports=(; out=Int), ta=2.0, make_out=true)
  151. # Receiver with TWO input ports
  152. rec = add_submodel!(top, :rec, MockNode;
  153. inbox=(; in_A=Int[], in_B=Int[])
  154. )
  155. connect!(top, sA.out, rec.in_A)
  156. connect!(top, sB.out, rec.in_B)
  157. model = compile_devs(top; logging=julia_test_file)
  158. simulate_devs!(model, 3.0)
  159. test_trace_equivalence(julia_test_file, "test/traces/ComplexBag.csv")
  160. node = model[model.path_to_id["top.rec"]]
  161. @test node.n_external == 1 # Only ONE external transition is fired for both!
  162. # Ensure both ports have data in the bag
  163. @test length(node.inbox_log[:in_A]) == 1
  164. @test length(node.inbox_log[:in_B]) == 1
  165. end