dfsm: Redefine arithmetic to saturate rather than overflow
[bendy-bus:bendy-bus.git] / dfsm / tests / simulation.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * D-Bus Simulator
4  * Copyright (C) Philip Withnall 2011 <philip@tecnocode.co.uk>
5  * 
6  * D-Bus Simulator is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * D-Bus Simulator is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with D-Bus Simulator.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <dfsm/dfsm.h>
21
22 #include "test-output-sequence.h"
23 #include "test-utils.h"
24
25 static GPtrArray/*<DfsmObject>*/ *
26 build_machine_description_from_transition_snippet (const gchar *snippet, GError **error)
27 {
28         gchar *machine_description, *introspection_xml;
29         GPtrArray/*<DfsmObject>*/ *object_array;
30         const gchar *introspection_xml_filename = "simple-test.xml";
31
32         machine_description = g_strdup_printf (
33                 "object at /uk/ac/cam/cl/DBusSimulator/ParserTest implements uk.ac.cam.cl.DBusSimulator.SimpleTest {"
34                         "data {"
35                                 "ArbitraryProperty = \"foo\";"
36                                 "Counter = 100u;"
37                                 "Random1Counter = 0u;"
38                                 "Random2Counter = 0u;"
39                                 "SingleEcho1Counter = 0u;"
40                                 "SingleEcho2Counter = 0u;"
41                                 "TwoEchoCounter = 0u;"
42                                 "ArbitraryPropertySetCounter = 0u;"
43                         "}"
44                         "states {"
45                                 "Main;"
46                         "}"
47                         "%s"
48                 "}", snippet);
49         introspection_xml = load_test_file (introspection_xml_filename);
50
51         object_array = dfsm_object_factory_from_files (machine_description, introspection_xml, error);
52
53         g_free (introspection_xml);
54         g_free (machine_description);
55
56         return object_array;
57 }
58
59 static void
60 test_simulation_probabilities (void)
61 {
62         GPtrArray/*<DfsmObject>*/ *simulated_objects;
63         DfsmObject *simulated_object;
64         DfsmMachine *machine;
65         DfsmEnvironment *environment;
66         GVariant *params, *val;
67         guint i;
68         GError *error = NULL;
69
70         #define TEST_COUNT 10000
71         #define DELTA 100
72
73         /* We build a simulation with several types of parallelism:
74          *  • Two arbitrarily triggered transitions (Random1 and Random2) which should be chosen between uniformly when a random trigger occurs.
75          *  • Two method triggered transitions (SingleEcho1 and SingleEcho2) which should be chosen between uniformly when SingleStateEcho is called.
76          *  • A single method triggered transition (TwoEcho) which should always be executed when TwoStateEcho is called.
77          *  • A single property triggered transition (ArbitraryPropertySet) which should be executed roughly half of the time it's called, since it's
78          *    predicated on the counter.
79          *
80          * All transitions update counters inside the simulation, which we later check by querying the environment.
81          */
82         simulated_objects = build_machine_description_from_transition_snippet (
83                 "transition Random1 inside Main on random {"
84                         "object->Counter = object->Counter + 1u;"
85                         "object->Random1Counter = object->Random1Counter + 1u;"
86                 "}"
87                 "transition Random2 inside Main on random {"
88                         "object->Counter = object->Counter - 1u;"
89                         "object->Random2Counter = object->Random2Counter + 1u;"
90                 "}"
91                 "transition SingleEcho1 inside Main on method SingleStateEcho {"
92                         "reply (\"reply\");"
93                         "object->SingleEcho1Counter = object->SingleEcho1Counter + 1u;"
94                 "}"
95                 "transition SingleEcho2 inside Main on method SingleStateEcho {"
96                         "reply (\"reply\");"
97                         "object->SingleEcho2Counter = object->SingleEcho2Counter + 1u;"
98                 "}"
99                 "transition TwoEcho inside Main on method TwoStateEcho {"
100                         "reply (\"reply\");"
101                         "object->TwoEchoCounter = object->TwoEchoCounter + 1u;"
102                 "}"
103                 "transition ArbitraryPropertySet inside Main on property ArbitraryProperty {"
104                         "precondition { object->Counter % 2u == 0u }"
105                         "object->ArbitraryProperty = value;"
106                         "object->ArbitraryPropertySetCounter = object->ArbitraryPropertySetCounter + 1u;"
107                 "}", &error);
108         g_assert_no_error (error);
109         g_assert_cmpuint (simulated_objects->len, ==, 1);
110
111         simulated_object = g_ptr_array_index (simulated_objects, 0);
112         machine = dfsm_object_get_machine (simulated_object);
113
114         environment = g_object_ref (dfsm_machine_get_environment (machine));
115
116         params = g_variant_ref_sink (new_unary_tuple (g_variant_new_string ("param")));
117         val = g_variant_ref_sink (g_variant_new_string ("value"));
118
119         for (i = 0; i < TEST_COUNT; i++) {
120                 DfsmOutputSequence *output_sequence;
121
122                 /* Make a random transition, call SingleStateEcho, call TwoStateEcho and then set ArbitraryProperty. */
123                 output_sequence = test_output_sequence_new (ENTRY_NONE);
124                 dfsm_machine_make_arbitrary_transition (machine, output_sequence, TRUE);
125                 g_object_unref (output_sequence);
126
127                 output_sequence = test_output_sequence_new (ENTRY_REPLY, new_unary_tuple (g_variant_new_string ("reply")), ENTRY_NONE);
128                 dfsm_machine_call_method (machine, output_sequence, "uk.ac.cam.cl.DBusSimulator.SimpleTest", "SingleStateEcho", params, TRUE);
129                 g_object_unref (output_sequence);
130
131                 output_sequence = test_output_sequence_new (ENTRY_REPLY, new_unary_tuple (g_variant_new_string ("reply")), ENTRY_NONE);
132                 dfsm_machine_call_method (machine, output_sequence, "uk.ac.cam.cl.DBusSimulator.SimpleTest", "TwoStateEcho", params, TRUE);
133                 g_object_unref (output_sequence);
134
135                 output_sequence = test_output_sequence_new (ENTRY_NONE);
136                 dfsm_machine_set_property (machine, output_sequence, "uk.ac.cam.cl.DBusSimulator.SimpleTest", "ArbitraryProperty", val, TRUE);
137                 g_object_unref (output_sequence);
138         }
139
140         g_variant_unref (val);
141         g_variant_unref (params);
142         g_ptr_array_unref (simulated_objects);
143
144         /* Check the counters. We expect:
145          *  • Counter to be near 100, since equal probability transitions incremented and decremented it.
146          *  • Random1Counter + Random2Counter == TEST_COUNT.
147          *  • Random1Counter and Random2Counter to both be roughly TEST_COUNT/2.
148          *  • SingleEcho1Counter + SingleEcho2Counter == TEST_COUNT.
149          *  • SingleEcho1Counter and SingleEcho2Counter to both be roughly TEST_COUNT/2.
150          *  • TwoEchoCounter == TEST_COUNT.
151          *  • ArbitraryPropertySetCounter to be roughly TEST_COUNT/2.
152          */
153 #define ASSERT_IN_RANGE(CounterName, Expectation, Delta) G_STMT_START { \
154                 guint __counter = get_counter_from_environment (environment, (CounterName)); \
155                 g_assert_cmpuint (__counter, >=, (Expectation) - (Delta)); \
156                 g_assert_cmpuint (__counter, <=, (Expectation) + (Delta)); \
157         } G_STMT_END
158
159         ASSERT_IN_RANGE ("Counter", 100, DELTA);
160         ASSERT_IN_RANGE ("Random1Counter", TEST_COUNT / 2, DELTA);
161         ASSERT_IN_RANGE ("Random2Counter", TEST_COUNT / 2, DELTA);
162         ASSERT_IN_RANGE ("SingleEcho1Counter", TEST_COUNT / 2, DELTA);
163         ASSERT_IN_RANGE ("SingleEcho2Counter", TEST_COUNT / 2, DELTA);
164         ASSERT_IN_RANGE ("TwoEchoCounter", TEST_COUNT, 0);
165         ASSERT_IN_RANGE ("ArbitraryPropertySetCounter", TEST_COUNT / 2, DELTA);
166
167         g_assert_cmpuint (get_counter_from_environment (environment, "Random1Counter") +
168                           get_counter_from_environment (environment, "Random2Counter"), ==, TEST_COUNT);
169         g_assert_cmpuint (get_counter_from_environment (environment, "SingleEcho1Counter") +
170                           get_counter_from_environment (environment, "SingleEcho2Counter"), ==, TEST_COUNT);
171
172 #undef ASSERT_IN_RANGE
173
174         g_object_unref (environment);
175 }
176
177 int
178 main (int argc, char *argv[])
179 {
180         g_type_init ();
181 #if !GLIB_CHECK_VERSION (2, 31, 0)
182         g_thread_init (NULL);
183 #endif
184         g_test_init (&argc, &argv, NULL);
185
186         g_test_add_func ("/simulation/probabilities", test_simulation_probabilities);
187
188         return g_test_run ();
189 }