Snej69420 před 5 měsíci
rodič
revize
fda4a47e62
4 změnil soubory, kde provedl 102 přidání a 15 odebrání
  1. 64 0
      test/compare.jl
  2. 19 4
      test/test_connections.jl
  3. 1 1
      test/test_miscellaneous.jl
  4. 18 10
      test/test_transitions.jl

+ 64 - 0
test/compare.jl

@@ -0,0 +1,64 @@
+using Test
+using CSV
+using DataFrames
+
+function test_trace_equivalence(jl_trace_path::String, py_trace_path::String; atol=1e-5)
+    jl_df = CSV.read(jl_trace_path, DataFrame)
+    py_df = CSV.read(py_trace_path, DataFrame)
+
+    sort!(jl_df, [:time, :model_path, :transition_type])
+    sort!(py_df, [:time, :model_path, :transition_type])
+
+    #  Check Row Count First
+    if nrow(jl_df) != nrow(py_df)
+        println("\nROW COUNT MISMATCH!")
+        println("Julia generated $(nrow(jl_df)) events, Python generated $(nrow(py_df)) events.")
+        @test nrow(jl_df) == nrow(py_df)
+        return
+    end
+
+    # Check Line by Line
+    for i in 1:nrow(jl_df)
+        jl_row = jl_df[i, :]
+        py_row = py_df[i, :]
+
+        step_info = "Row $i | Model: $(jl_row.model_path) | Trans: $(jl_row.transition_type)"
+
+        # Mathematical Time Equivalence
+        if !isapprox(jl_row.time, py_row.time, atol=atol)
+            println("\nTIME MISMATCH at $step_info")
+            println("\tJulia: $(jl_row.time) != Python: $(py_row.time)")
+        end
+        @test isapprox(jl_row.time, py_row.time, atol=atol)
+
+        # Next Time Advance
+        if py_row.next_ta != "inf" && jl_row.next_ta != "inf"
+            jl_ta = parse(Float64, string(jl_row.next_ta))
+            py_ta = parse(Float64, string(py_row.next_ta))
+            if !isapprox(jl_ta, py_ta, atol=atol)
+                println("\nTIME ADVANCE MISMATCH at $step_info")
+                println("\tJulia: $jl_ta != Python: $py_ta")
+            end
+            @test isapprox(jl_ta, py_ta, atol=atol)
+        else
+            @test string(jl_row.next_ta) == string(py_row.next_ta)
+        end
+
+        # Routing & Logic Equivalence
+        @test jl_row.model_path == py_row.model_path
+        @test string(jl_row.transition_type) == string(py_row.transition_type)
+
+        # Bag Equivalence
+        if jl_row.inputs != py_row.inputs
+            println("\nINPUT MISMATCH at $step_info")
+            println("\tJulia: $(jl_row.inputs) != Python: $(py_row.inputs)")
+        end
+        @test jl_row.inputs == py_row.inputs
+
+        if jl_row.outputs != py_row.outputs
+            println("\nOUTPUT MISMATCH at $step_info")
+            println("\tJulia: $(jl_row.outputs) != Python: $(py_row.outputs)")
+        end
+        @test jl_row.outputs == py_row.outputs
+    end
+end

+ 19 - 4
test/test_connections.jl

@@ -1,3 +1,14 @@
+function routes_to(model, source_id::Int, source_port::Symbol, target_id::Int, target_port::Symbol)
+    source_key = (source_id, source_port)
+
+    if !haskey(model.couplings, source_key)
+        return false
+    end
+
+    targets = model.couplings[source_key]
+    return any(t -> t[1] == target_id && t[2] == target_port, targets)
+end
+
 @testset "Self Loops" begin
     top = CoupledDEVS(:TopModel)
     add_in_port!(top, :in)
@@ -39,11 +50,13 @@ end
 
     # Check A -> B exists
     @test haskey(model.couplings, (id_A, :out))
-    @test (id_B, :in) in model.couplings[(id_A, :out)]
+    @test routes_to(model, id_A, :out, id_B, :in)
+#     @test (id_B, :in) in model.couplings[(id_A, :out)]
 
     # Check B -> A exists
     @test haskey(model.couplings, (id_B, :out))
-    @test (id_A, :in) in model.couplings[(id_B, :out)]
+    @test routes_to(model, id_B, :out, id_A, :in)
+#     @test (id_A, :in) in model.couplings[(id_B, :out)]
 end
 
 @testset "Atomic Input Couplings" begin
@@ -126,7 +139,8 @@ end
     atomic2_id = model.path_to_id["top.atomic2"]
 
     @test haskey(model.couplings, (atomic1_id, :out))
-    @test (atomic2_id, :in) in model.couplings[(atomic1_id, :out)]
+    @test routes_to(model, atomic1_id, :out, atomic2_id, :in)
+#     @test (atomic2_id, :in) in model.couplings[(atomic1_id, :out)]
     empty!(top.couplings)
 
     # atomic out -> coupled in
@@ -190,5 +204,6 @@ end
     receiver_id = model.path_to_id["parent.child2.receiver"]
 
     @test haskey(model.couplings, (sender_id, :out))
-    @test (receiver_id, :in) in model.couplings[(sender_id, :out)]
+#     @test (receiver_id, :in) in model.couplings[(sender_id, :out)]
+    @test routes_to(model, sender_id, :out, receiver_id, :in)
 end

+ 1 - 1
test/test_miscellaneous.jl

@@ -29,7 +29,7 @@ end
         out_ports=Set([:out]), ta=1.0
     )
 
-    model = compile_devs(top; logging=true)
+    model = compile_devs(top; logging="")
     simulate_devs!(model, 2.0)
 
     @test length(model.event_logs) > 0

+ 18 - 10
test/test_transitions.jl

@@ -1,3 +1,5 @@
+julia_test_file = "test/traces/julia.csv"
+
 @testset "Transient State (ta = 0.0)" begin
     top = CoupledDEVS(:top)
 
@@ -68,8 +70,9 @@ end
         ta=2.0
     )
 
-    model = compile_devs(top)
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model)
+    test_trace_equivalence(julia_test_file, "test/traces/Internal.csv")
 
     receiver = model[model.path_to_id["top.receiver"]]
 
@@ -90,8 +93,9 @@ end
     )
     connect!(top, sender_ref.out, receiver_ref.in)
 
-    model = compile_devs(top)
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model)
+    test_trace_equivalence(julia_test_file, "test/traces/External.csv")
 
     sender = model[model.path_to_id["top.sender"]]
     @test sender.n_internal == 1
@@ -112,8 +116,9 @@ end
     receiver_ref = add_submodel!(top, :receiver, MockNode; inbox=(; in=Int[]), ta=5.0)
     connect!(top, sender_ref.out, receiver_ref.in)
 
-    model = compile_devs(top)
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model, 2.0)
+    test_trace_equivalence(julia_test_file, "test/traces/ElapsedTimeInterruption.csv")
 
     receiver = model[model.path_to_id["top.receiver"]]
     @test receiver.n_external == 1
@@ -123,21 +128,22 @@ end
 @testset "Elapsed Time Reset (Multiple Interruptions)" begin
     top = CoupledDEVS(:top)
 
-    # Sender 1 fires at t = 3.0
     s1 = add_submodel!(top, :s1, MockNode; out_ports=Set([:out]), ta=3.0, make_out=true)
-    # Sender 2 fires at t = 5.0
     s2 = add_submodel!(top, :s2, MockNode; out_ports=Set([:out]), ta=5.0, make_out=true)
 
     rec = add_submodel!(top, :rec, MockNode; inbox=(; in=Int[]), ta=10.0)
 
     connect!(top, s1.out, rec.in)
     connect!(top, s2.out, rec.in)
-    model = compile_devs(top)
 
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model, 4.0)
+    test_trace_equivalence(julia_test_file, "test/traces/ElapsedTimeReset1.csv")
+
     @test model[model.path_to_id["top.rec"]].last_e == 3.0
 
     simulate_devs!(model, 6.0)
+    test_trace_equivalence(julia_test_file, "test/traces/ElapsedTimeReset2.csv")
     @test model[model.path_to_id["top.rec"]].last_e == 2.0
 end
 
@@ -153,9 +159,10 @@ end
         ta=2.0
     )
     connect!(top, sender_ref.out, receiver_ref.in)
-    model = compile_devs(top)
 
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model)
+    test_trace_equivalence(julia_test_file, "test/traces/Confluent.csv")
 
     sender = model[model.path_to_id["top.sender"]]
     @test sender.n_internal == 1
@@ -173,16 +180,17 @@ end
 
     sender_ref = add_submodel!(top, :sender, MockNode;
         out_ports=Set([:out]),
-        ta=1.999999, make_out=true
+        ta=1.999, make_out=true
     )
     receiver_ref = add_submodel!(top, :receiver, MockNode;
         inbox=(; in=Int[]),
-        ta=2.000000
+        ta=2.000
     )
     connect!(top, sender_ref.out, receiver_ref.in)
-    model = compile_devs(top)
 
+    model = compile_devs(top; logging=julia_test_file)
     simulate_devs!(model)
+    test_trace_equivalence(julia_test_file, "test/traces/NearlyConfluent.csv")
 
     sender = model[model.path_to_id["top.sender"]]
     @test sender.n_internal == 1