瀏覽代碼

Merge branch 'testing' into stable

Yentl Van Tendeloo 8 年之前
父節點
當前提交
5573492574
共有 7 個文件被更改,包括 893 次插入8 次删除
  1. 688 0
      integration/code/SCCD_all.mvc
  2. 9 0
      models/SCCD_Trace.mvc
  3. 27 7
      models/SCCD_execute.alc
  4. 16 0
      scripts/run_basic_tests.py
  5. 65 0
      unit/test_all.py
  6. 82 0
      unit/utils.py
  7. 6 1
      wrappers/modelverse.py

+ 688 - 0
integration/code/SCCD_all.mvc

@@ -0,0 +1,688 @@
+include "primitives.alh"
+
+Diagram dynamic_trafficlight {
+    name = "Dynamic Traffic Light"
+    author = "Yentl Van Tendeloo"
+}
+
+Class manager {
+    name = "Manager"
+    default = True
+    constructor_body = $
+            Void function constructor(attributes : Element, parameters : Element):
+                dict_add(attributes, "trafficlights", list_create())
+                return !
+        $
+
+    {behaviour} CompositeState manager_main {
+        name = "main"
+        isInitial = True
+        {composite_children} ParallelState manager_main_parallel {
+            name = "parallel"
+            isInitial = True
+
+            {parallel_children} CompositeState manager_main_parallel_core {
+                name = "core"
+                isInitial = True
+
+                {composite_children} BasicState manager_main_parallel_core_start {
+                    name = "start"
+                    isInitial = True
+                }
+            }
+
+            {parallel_children} CompositeState manager_main_parallel_input {
+                name = "input"
+                isInitial = True
+
+                {composite_children} BasicState manager_main_parallel_input_1 {
+                    name = "1"
+                    isInitial = True
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_2 {
+                    name = "2"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_3 {
+                    name = "3"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_4 {
+                    name = "4"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_5 {
+                    name = "5"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_6 {
+                    name = "6"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_7 {
+                    name = "7"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_8 {
+                    name = "8"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_9 {
+                    name = "9"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_10 {
+                    name = "10"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_11 {
+                    name = "11"
+                    isInitial = False
+                }
+
+                {composite_children} BasicState manager_main_parallel_input_12 {
+                    name = "12"
+                    isInitial = False
+                }
+            }
+        }
+    }
+}
+
+transition (manager_main_parallel_input_1, manager_main_parallel_input_2) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 1.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "create"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_2, manager_main_parallel_input_3) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 4.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_3, manager_main_parallel_input_4) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 10.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "create"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_4, manager_main_parallel_input_5) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "police_interrupt"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_5, manager_main_parallel_input_6) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 9.4!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_6, manager_main_parallel_input_7) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 100.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "toggle"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_7, manager_main_parallel_input_8) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 10.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "police_interrupt"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_8, manager_main_parallel_input_9) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "delete"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_9, manager_main_parallel_input_10) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 4.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "delete"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_input_10, manager_main_parallel_input_11) {
+    name = ""
+    after = $
+                Element function after(attributes : Element):
+                    return 5.0!
+            $
+
+    {transition_raises} Raise {
+        scope = "broad"
+        event = "exit"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    return list_create()!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "create"
+    event = "create"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "create_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, "trafficlights")
+                    list_append(result, "TrafficLight")
+                    list_append(result, read_root())
+                    return result!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "created"
+    event = "instance_created"
+
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                list_append(attributes["trafficlights"], parameters[0])
+                return!
+        $
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "exit"
+    event = "exit"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "delete_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, "0")
+                    return result!
+            $
+    }
+}
+
+transition (manager_main_parallel_core_start, manager_main_parallel_core_start) {
+    name = "delete"
+    event = "delete"
+
+    {transition_raises} Raise {
+        scope = "cd"
+        event = "delete_instance"
+        parameter = $
+                Element function raise(attributes : Element, parameters : Element):
+                    Element result
+                    result = list_create()
+                    list_append(result, list_pop_final(attributes["trafficlights"]))
+                    return result!
+            $
+    }
+}
+
+Class trafficlight {
+    name = "TrafficLight"
+    default = False
+    constructor_body = $
+            Void function constructor(attributes : Element, parameters : Element):
+                dict_add(attributes, "counter", 0)
+                dict_add(attributes, "colour", "")
+                return!
+        $
+
+    {behaviour} CompositeState trafficlight_main {
+        name = "main"
+        isInitial = True
+        {composite_children} BasicState trafficlight_main_off {
+            name = "off"
+            isInitial = True
+        }
+
+        {composite_children} ParallelState trafficlight_main_main {
+            name = "main"
+            isInitial = False
+
+            {onExitRaise} Raise {
+                event = "displayNone"
+                scope = "output"
+            }
+
+            {parallel_children} CompositeState trafficlight_main_main_trafficlight {
+                name = "trafficlight"
+                isInitial = False
+
+                {composite_children} CompositeState trafficlight_main_main_trafficlight_normal {
+                    name = "normal"
+                    isInitial = True
+
+                    onExitScript = $
+                            Void function onexit(attributes : Element):
+                                dict_overwrite(attributes, "colour", "")
+                                return!
+                        $
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_red {
+                        name = "red"
+                        isInitial = True
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "counter", 60)
+                                    dict_overwrite(attributes, "colour", "red")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayRed"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "resetTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_green {
+                        name = "green"
+                        isInitial = False
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "counter", 55)
+                                    dict_overwrite(attributes, "colour", "green")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayGreen"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "resetTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_normal_yellow {
+                        name = "yellow"
+                        isInitial = False
+
+                        onEntryScript = $
+                                Void function onentry(attributes : Element):
+                                    dict_overwrite(attributes, "colour", "")
+                                    return!
+                            $
+
+                        {onEntryRaise} Raise {
+                            event = "displayYellow"
+                            scope = "output"
+                        }
+                        {onEntryRaise} Raise {
+                            event = "disableTimer"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} HistoryState trafficlight_main_main_trafficlight_normal_hist {
+                        name = "hist"
+                    }
+                }
+
+                {composite_children} CompositeState trafficlight_main_main_trafficlight_interrupted {
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_interrupted_yellow {
+                        name = "yellow"
+                        isInitial = True
+
+                        {onEntryRaise} Raise {
+                            event = "displayYellow"
+                            scope = "output"
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_trafficlight_interrupted_black {
+                        name = "black"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "displayNone"
+                            scope = "output"
+                        }
+                    }
+                }
+            }
+
+            {parallel_children} CompositeState trafficlight_main_main_timer {
+                name = "timer"
+                isInitial = False
+
+                {composite_children} BasicState trafficlight_main_main_timer_disabled {
+                    name = "disabled"
+                    isInitial = False
+                }
+
+                {composite_children} CompositeState trafficlight_main_main_timer_running {
+                    name = "running"
+                    isInitial = True
+
+                    {onExitRaise} Raise {
+                        event = "updateTimerValue"
+                        scope = "output"
+                        parameter = $
+                                Element function parameter(attributes : Element):
+                                    return -1!
+                            $
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_decidingcolor {
+                        name = "decidingcolor"
+                        isInitial = True
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_green {
+                        name = "green"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "updateTimerValue"
+                            scope = "output"
+                            parameter = $
+                                    Element function raise(attributes : Element):
+                                        return attributes["counter"]!
+                                $
+                        }
+                    }
+
+                    {composite_children} BasicState trafficlight_main_main_timer_running_red {
+                        name = "red"
+                        isInitial = False
+
+                        {onEntryRaise} Raise {
+                            event = "updateTimerValue"
+                            parameter = $
+                                    Element function raise(attributes : Element):
+                                        return attributes["counter"]!
+                                $
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+transition (trafficlight_main_off, trafficlight_main_main) {
+    name = "toggle"
+    event = "toggle"
+}
+
+transition (trafficlight_main_main, trafficlight_main_off) {
+    name = "toggle"
+    event = "toggle"
+}
+
+transition (trafficlight_main_main_trafficlight_normal, trafficlight_main_main_trafficlight_interrupted) {
+    name = "police_interrupt / disableTimer"
+    event = "police_interrupt"
+
+    {transition_raises} Raise {
+        event = "disableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted, trafficlight_main_main_trafficlight_normal_hist) {
+    name = "police_interrupt / enableTimer"
+    event = "police_interrupt"
+
+    {transition_raises} Raise {
+        event = "enableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_normal_red, trafficlight_main_main_trafficlight_normal_green) {
+    name = "after 60s"
+    after = $
+            Float function after(attributes : Element):
+                return 60.0!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_normal_green, trafficlight_main_main_trafficlight_normal_yellow) {
+    name = "after 55s"
+    after = $
+            Float function after(attributes : Element):
+                return 55.0!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_normal_yellow, trafficlight_main_main_trafficlight_normal_red) {
+    name = "after 5s"
+    after = $
+            Float function after(attributes : Element):
+                return 5.0!
+        $
+
+    {transition_raises} Raise {
+        event = "enableTimer"
+    }
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted_yellow, trafficlight_main_main_trafficlight_interrupted_black) {
+    name = "after 500ms"
+    after = $
+            Float function after(attributes : Element):
+                return 0.5!
+        $
+}
+
+transition (trafficlight_main_main_trafficlight_interrupted_black, trafficlight_main_main_trafficlight_interrupted_yellow) {
+    name = "after 500ms"
+    after = $
+            Float function after(attributes : Element):
+                return 0.5!
+        $
+}
+
+transition (trafficlight_main_main_timer_disabled, trafficlight_main_main_timer_running) {
+    name = "enableTimer"
+    event = "enableTimer"
+}
+
+transition (trafficlight_main_main_timer_running, trafficlight_main_main_timer_disabled) {
+    name = "disableTimer"
+    event = "disableTimer"
+}
+
+transition (trafficlight_main_main_timer_running, trafficlight_main_main_timer_running) {
+    name = "resetTimer"
+    event = "resetTimer"
+}
+
+transition (trafficlight_main_main_timer_running_decidingcolor, trafficlight_main_main_timer_running_green) {
+    name = "[green] / updateTimerColour"
+    cond = $
+            Boolean function cond(attributes : Element, parameters : Element):
+                return value_eq(attributes["colour"], "green")!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerColour"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return "green"!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_decidingcolor, trafficlight_main_main_timer_running_red) {
+    name = "[red] / updateTimerColour"
+    cond = $
+            Boolean function cond(attributes : Element, parameters : Element):
+                return value_eq(attributes["colour"], "red")!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerColour"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return "red"!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_red, trafficlight_main_main_timer_running_red) {
+    name = "after 1s / counter -= 1"
+    after = $
+            Float function after(attributes : Element):
+                return 1.0!
+        $
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                dict_overwrite(attributes, "counter", integer_subtraction(attributes["counter"], 1))
+                return!
+        $
+
+    {transition_raises} Raise {
+        event = "updateTimerValue"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return attributes["counter"]!
+            $
+    }
+}
+
+transition (trafficlight_main_main_timer_running_green, trafficlight_main_main_timer_running_green) {
+    name = "after 1s / counter -= 1"
+    after = $
+            Float function after(attributes : Element):
+                return 1.0!
+        $
+    script = $
+            Void function script(attributes : Element, parameters : Element):
+                dict_overwrite(attributes, "counter", integer_subtraction(attributes["counter"], 1))
+                return!
+        $
+    {transition_raises} Raise {
+        event = "updateTimerValue"
+        scope = "output"
+        parameter = $
+                Element function param(attributes : Element, parameters : Element):
+                    return attributes["counter"]!
+            $
+    }
+}

+ 9 - 0
models/SCCD_Trace.mvc

@@ -0,0 +1,9 @@
+SimpleAttribute Float {}
+SimpleAttribute String {}
+
+Class Event{
+    timestamp : Float
+    name : String
+    parameter : String
+}
+

+ 27 - 7
models/SCCD_execute.alc

@@ -193,11 +193,15 @@ String function start_class(model : Element, data : Element, class : String, par
 
 	execute_actions(model, init, set_copy(class_handle["states"]), data, "")
 
+	// Notify this class itself of its ID
+	Element lst
+	lst = list_create()
+	list_append(lst, identifier)
+	set_add_node(data["current_class_handle"]["new_events"], create_tuple("instance_created", lst))
+
 	dict_overwrite(data, "current_class", prev_class)
 	dict_overwrite(data, "current_class_handle", data["classes"][prev_class])
 
-	log("Created class with identifier: " + cast_v2s(identifier))
-
 	return identifier!
 
 Element function get_enabled_transitions(model : Element, state : String, data : Element):
@@ -304,6 +308,8 @@ Void function process_raised_event(model : Element, event : Element, parameter_a
 			class = set_pop(filter(model, allInstances(model, "SCCD/Class"), "name", list_read(parameter_action, 1)))
 			parameters = list_read(parameter_action, 2)
 			identifier = start_class(model, data, class, parameters)
+
+			// Notify the creator of the ID of the created instance
 			Element lst
 			lst = list_create()
 			list_append(lst, identifier)
@@ -315,23 +321,32 @@ Void function process_raised_event(model : Element, event : Element, parameter_a
 			identifier = list_read(parameter_action, 0)
 			delete_class(model, data, identifier)
 			set_add_node(data["current_class_handle"]["new_events"], create_tuple("instance_deleted", parameter_action))
+
 	elif (scope == "broad"):
 		// Send to all classes
 		Element classes
 		classes = dict_keys(data["classes"])
 		while(set_len(classes) > 0):
 			set_add_node(data["classes"][set_pop(classes)]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
+
 	elif (scope == "narrow"):
 		// Send to the specified class only
 		Element func
 		func = resolve_function(read_attribute(model, event, "target"), data)
 		String dest
 		dest = func(data["current_class_handle"]["attributes"])
-		log("Narrowcasting to " + cast_v2s(dest))
 		set_add_node(data["classes"][dest]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
+
+	elif (scope == "output"):
+		// Store it in the output event model
+		String evt
+		evt = instantiate_node(model, "trace/Event", "")
+		instantiate_attribute(model, evt, "timestamp", data["time_sim"])
+		instantiate_attribute(model, evt, "name", read_attribute(model, event, "event"))
+		instantiate_attribute(model, evt, "parameter", parameter_action)
+
 	else:
 		// Same as local
-		log("Unknown scope, assuming local: " + scope)
 		set_add_node(data["current_class_handle"]["new_events"], create_tuple(read_attribute(model, event, "event"), parameter_action))
 
 	return !
@@ -721,6 +736,10 @@ Boolean function main(model : Element):
 	time_sim = 0.0
 	dict_add(data, "time_sim", 0.0)
 
+	Boolean afap
+	// NOTE this can be changed to True for testing
+	afap = False
+
 	// Prepare for input
 	output("Ready for input!")
 
@@ -735,6 +754,8 @@ Boolean function main(model : Element):
 	Element interrupt
 	timeout = 0.0
 	while (True):
+		if (afap):
+			timeout = 0.0
 		interrupt = input_timeout(timeout)
 
 		if (value_eq(interrupt, "#EXIT#")):
@@ -776,7 +797,6 @@ Boolean function main(model : Element):
 
 		time_wallclock = time() - time_0
 		timeout = time_sim - time_wallclock
-		log("Pause for: " + cast_v2s(timeout))
 
-	// We should never get here!
-	return False!
+	// We have finished without error
+	return True!

+ 16 - 0
scripts/run_basic_tests.py

@@ -0,0 +1,16 @@
+from flush_compiler_caches import flush_all
+from generate_bootstrap import bootstrap
+import subprocess
+import sys
+
+flush_all()
+
+bootstrap()
+
+subprocess.check_call([sys.executable, "-m", "pytest"], cwd="state")
+
+subprocess.check_call([sys.executable, "-m", "pytest"], cwd="kernel")
+
+subprocess.check_call([sys.executable, "-m", "pytest"], cwd="interface/HUTN")
+
+subprocess.check_call([sys.executable, "-m", "pytest", "unit", "-x", "-v"])

+ 65 - 0
unit/test_all.py

@@ -286,6 +286,71 @@ class TestModelverse(unittest.TestCase):
         model_delete("type mappings/rendered")
         model_delete("type mappings/tracability")
 
+    def test_SCCD_basic(self):
+        model_add("test/SCCD", "formalisms/SimpleClassDiagrams", open("models/SCCD.mvc", 'r').read())
+        model_add("test/SCCD_Trace", "formalisms/SimpleClassDiagrams", open("models/SCCD_Trace.mvc", 'r').read())
+
+        model_add("test/my_SCCD", "test/SCCD", open("integration/code/SCCD_all.mvc", 'r').read())
+
+        transformation_add_AL({"SCCD": "test/SCCD"}, {"trace": "test/SCCD_Trace"}, "test/SCCD_execute_afap", open("models/SCCD_execute.alc", 'r').read().replace("afap = False", "afap = True"))
+        transformation_execute_AL("test/SCCD_execute_afap", {"SCCD": "test/my_SCCD"}, {"trace": "test/my_SCCD_trace"})
+
+        alter_context("test/my_SCCD_trace", "test/SCCD_Trace")
+        lst = element_list_nice("test/my_SCCD_trace")
+
+        model_delete("merged")
+        model_delete("type mappings/merged")
+
+        lst.sort(key=lambda i: (i["timestamp"], i["name"]))
+        result = [(i["timestamp"], str(i["name"])) for i in lst if i["name"] not in ["updateTimerValue", "updateTimerColour", "resetTimer"]]
+        print(result)
+
+        assert result == [(5.0, "displayRed"),
+                          (20.0, "displayYellow"),
+                          (20.5, "displayNone"),
+                          (21.0, "displayYellow"),
+                          (21.5, "displayNone"),
+                          (22.0, "displayYellow"),
+                          (22.5, "displayNone"),
+                          (23.0, "displayYellow"),
+                          (23.5, "displayNone"),
+                          (24.0, "displayYellow"),
+                          (24.5, "displayNone"),
+                          (25.0, "displayYellow"),
+                          (25.5, "displayNone"),
+                          (26.0, "displayYellow"),
+                          (26.5, "displayNone"),
+                          (27.0, "displayYellow"),
+                          (27.5, "displayNone"),
+                          (28.0, "displayYellow"),
+                          (28.5, "displayNone"),
+                          (29.0, "displayYellow"),
+                          (29.4, "displayNone"),
+                          (29.4, "displayRed"),
+                          (89.4, "displayGreen"),
+                          (129.4, "displayNone"),
+                          (129.4, "displayRed"),
+                          (139.4, "displayYellow"),
+                          (139.9, "displayNone"),
+                          (140.4, "displayYellow"),
+                          (140.9, "displayNone"),
+                          (141.4, "displayYellow"),
+                          (141.9, "displayNone"),
+                          (142.4, "displayYellow"),
+                          (142.9, "displayNone"),
+                          (143.4, "displayYellow"),
+                          (143.9, "displayNone"),
+                          (144.4, "displayYellow"),
+                          (144.9, "displayNone"),
+                          (145.4, "displayYellow"),
+                          (145.9, "displayNone"),
+                          (146.4, "displayYellow"),
+                          (146.9, "displayNone"),
+                          (147.4, "displayYellow"),
+                          (147.9, "displayNone"),
+                          (148.4, "displayYellow"),
+                         ]
+
     def test_switch_MM(self):
         model_add("test/PetriNet", "formalisms/SimpleClassDiagrams", open("integration/code/pn_design.mvc", "r").read())
         model_add("test/my_pn", "test/PetriNet", open("integration/code/pn_design_model.mvc", "r").read())

+ 82 - 0
unit/utils.py

@@ -0,0 +1,82 @@
+import unittest
+import sys
+import os
+
+import sys
+import time
+import json
+import urllib
+import urllib2
+import subprocess
+import signal
+import random
+
+sys.path.append("interface/HUTN")
+sys.path.append("scripts")
+from hutn_compiler.compiler import main as do_compile
+
+taskname = "test_task"
+INIT_TIMEOUT = 30
+
+try:
+    import pytest
+    slow = pytest.mark.skipif(
+        not pytest.config.getoption("--runslow"),
+            reason="need --runslow option to run"
+    )
+except:
+    slow = lambda i:i
+
+ports = set()
+
+def getFreePort():
+    """Gets a unique new port."""
+    while 1:
+        port = random.randint(10000, 20000)
+        # Check if this port is in the set of ports.
+        if port not in ports:
+            # We have found a unique port. Add it to the set and return.
+            ports.add(port)
+            return port
+
+def execute(scriptname, parameters=[], wait=False):
+    if os.name not in ["nt", "posix"]:
+        # Stop now, as we would have no clue on how to kill its subtree
+        raise Exception("Unknown OS version: " + str(os.name))
+
+    command = [sys.executable, "-u", "scripts/%s.py" % scriptname] + parameters
+
+    if wait:
+        return subprocess.call(command, shell=False)
+    else:
+        return subprocess.Popen(command, shell=False)
+
+def child_kill(pid):
+    subprocess.call(["pkill", "-P", "%i" % pid])
+    start = time.time()
+    with open(os.devnull, 'w') as null:
+        while subprocess.call(["pgrep", "-P", "%i" % pid], stdout=null) != 1:
+            time.sleep(0.1)
+            if time.time() > start + 4:
+                subprocess.call(["pkill", "-9", "-P", "%i" % pid])
+
+def kill(process):
+    if os.name == "nt":
+        #TODO this is Windows and I don't care too much about that...
+        subprocess.call(["taskkill", "/F", "/T", "/PID", "%i" % process.pid])
+    elif os.name == "posix":
+        child_kill(process.pid)
+        process.send_signal(signal.SIGINT)
+        process.wait()
+        child_kill(os.getpid())
+
+def flush_data(address, data):
+    if data:
+        urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": json.dumps(data), "taskname": taskname})), timeout=INIT_TIMEOUT).read()
+    return []
+
+def start_mvc():
+    port = getFreePort()
+    address = "http://127.0.0.1:%s" % port
+    proc = execute("run_local_modelverse", [str(port)], wait=False)
+    return proc, address

+ 6 - 1
wrappers/modelverse.py

@@ -938,7 +938,12 @@ def element_list_nice(model_name):
 
     _input(["element_list_nice", model_name, _get_metamodel(model_name)])
 
-    return json.loads(_handle_output("Success: ", split=" "))
+    data = _handle_output("Success: ", split=" ")
+    try:
+        return json.loads(data)
+    except:
+        print(data)
+        raise
 
 def connections_between(model_name, source_element, target_element):
     """Gets a list of all allowed connections between the source and target element in the model."""