Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018 Endless Mobile, Inc.
4 : : *
5 : : * This library is free software; you can redistribute it and/or
6 : : * modify it under the terms of the GNU Lesser General Public
7 : : * License as published by the Free Software Foundation; either
8 : : * version 2.1 of the License, or (at your option) any later version.
9 : : *
10 : : * This library is distributed in the hope that it will be useful,
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : : * Lesser General Public License for more details.
14 : : *
15 : : * You should have received a copy of the GNU Lesser General Public
16 : : * License along with this library; if not, write to the Free Software
17 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : *
19 : : * Authors:
20 : : * - Philip Withnall <withnall@endlessm.com>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include <glib.h>
26 : : #include <gio/gio.h>
27 : : #include <libmogwai-schedule/scheduler.h>
28 : : #include <libmogwai-schedule/tests/clock-dummy.h>
29 : : #include <libmogwai-schedule/tests/connection-monitor-dummy.h>
30 : : #include <libmogwai-schedule/tests/peer-manager-dummy.h>
31 : : #include <libmogwai-schedule/tests/signal-logger.h>
32 : : #include <libmogwai-tariff/tariff-builder.h>
33 : : #include <libmogwai-tariff/tariff.h>
34 : : #include <locale.h>
35 : :
36 : :
37 : : /* Fixture which creates a #MwsScheduler, connects it to a mock peer manager,
38 : : * and logs all its signal emissions in @scheduled_signals.
39 : : *
40 : : * @scheduler_signals must be empty when the fixture is torn down. */
41 : : typedef struct
42 : : {
43 : : MwsConnectionMonitor *connection_monitor; /* (owned) */
44 : : MwsPeerManager *peer_manager; /* (owned) */
45 : : MwsClock *clock; /* (owned) */
46 : : MwsScheduler *scheduler; /* (owned) */
47 : : MwsSignalLogger *scheduler_signals; /* (owned) */
48 : : } Fixture;
49 : :
50 : : typedef struct
51 : : {
52 : : guint max_active_entries; /* > 1 */
53 : : } TestData;
54 : :
55 : : static void
56 : 36 : setup (Fixture *fixture,
57 : : gconstpointer test_data)
58 : : {
59 : 36 : const TestData *data = test_data;
60 : :
61 [ - + ]: 36 : g_assert (data->max_active_entries > 0);
62 : :
63 : 36 : fixture->connection_monitor = MWS_CONNECTION_MONITOR (mws_connection_monitor_dummy_new ());
64 : 36 : fixture->peer_manager = MWS_PEER_MANAGER (mws_peer_manager_dummy_new (FALSE));
65 : 36 : fixture->clock = MWS_CLOCK (mws_clock_dummy_new ());
66 : :
67 : : /* Construct the scheduler manually so we can set max-active-entries. */
68 : 36 : fixture->scheduler = g_object_new (MWS_TYPE_SCHEDULER,
69 : : "connection-monitor", fixture->connection_monitor,
70 : : "peer-manager", fixture->peer_manager,
71 : : "clock", fixture->clock,
72 : 36 : "max-active-entries", data->max_active_entries,
73 : : NULL);
74 : 36 : fixture->scheduler_signals = mws_signal_logger_new ();
75 : 36 : mws_signal_logger_connect (fixture->scheduler_signals,
76 : 36 : fixture->scheduler, "notify::allow-downloads");
77 : 36 : mws_signal_logger_connect (fixture->scheduler_signals,
78 : 36 : fixture->scheduler, "notify::entries");
79 : 36 : mws_signal_logger_connect (fixture->scheduler_signals,
80 : 36 : fixture->scheduler, "entries-changed");
81 : 36 : mws_signal_logger_connect (fixture->scheduler_signals,
82 : 36 : fixture->scheduler, "active-entries-changed");
83 : 36 : }
84 : :
85 : : static void
86 : 36 : teardown (Fixture *fixture,
87 : : gconstpointer test_data)
88 : : {
89 : : /* Clear the signal logger first so we don’t accidentally log signals from
90 : : * the other objects while we finalise them. */
91 [ - + ]: 36 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
92 [ + - ]: 36 : g_clear_pointer (&fixture->scheduler_signals, mws_signal_logger_free);
93 : :
94 [ + - ]: 36 : g_clear_object (&fixture->scheduler);
95 [ + - ]: 36 : g_clear_object (&fixture->clock);
96 [ + - ]: 36 : g_clear_object (&fixture->peer_manager);
97 [ + - ]: 36 : g_clear_object (&fixture->connection_monitor);
98 : 36 : }
99 : :
100 : : /**
101 : : * assert_ptr_arrays_equal:
102 : : * @array1: (nullable): an array
103 : : * @array2: (nullable): another array
104 : : *
105 : : * Compare @array1 and @array2, checking that all their elements are pointerwise
106 : : * equal.
107 : : *
108 : : * Passing %NULL to either parameter is equivalent to passing an empty array.
109 : : *
110 : : * If the arrays do not match, an assertion will fail.
111 : : *
112 : : * Since: 0.1.0
113 : : */
114 : : static void
115 : 474 : assert_ptr_arrays_equal (GPtrArray *array1,
116 : : GPtrArray *array2)
117 : : {
118 [ + + + + ]: 474 : gboolean array1_is_empty = (array1 == NULL || array1->len == 0);
119 [ + + - + ]: 474 : gboolean array2_is_empty = (array2 == NULL || array2->len == 0);
120 : :
121 [ - + ]: 474 : g_assert_cmpuint (array1_is_empty, ==, array2_is_empty);
122 : :
123 [ + + - + ]: 474 : if (array1_is_empty || array2_is_empty)
124 : 343 : return;
125 : :
126 [ - + ]: 131 : g_assert_cmpuint (array1->len, ==, array2->len);
127 : :
128 [ + + ]: 285 : for (gsize i = 0; i < array1->len; i++)
129 [ - + ]: 154 : g_assert_true (g_ptr_array_index (array1, i) == g_ptr_array_index (array2, i));
130 : : }
131 : :
132 : : /* Test that constructing a #MwsScheduler works. A basic smoketest. */
133 : : static void
134 : 1 : test_scheduler_construction (void)
135 : : {
136 : 1 : g_autoptr(MwsConnectionMonitor) connection_monitor = NULL;
137 : 1 : connection_monitor = MWS_CONNECTION_MONITOR (mws_connection_monitor_dummy_new ());
138 : :
139 : 1 : g_autoptr(MwsPeerManager) peer_manager = NULL;
140 : 1 : peer_manager = MWS_PEER_MANAGER (mws_peer_manager_dummy_new (FALSE));
141 : :
142 : 2 : g_autoptr(MwsClock) clock = MWS_CLOCK (mws_clock_dummy_new ());
143 : :
144 : 2 : g_autoptr(MwsScheduler) scheduler = mws_scheduler_new (connection_monitor,
145 : : peer_manager,
146 : : clock);
147 : :
148 : : /* Do something to avoid the compiler warning about unused variables. */
149 [ - + ]: 1 : g_assert_true (mws_scheduler_get_peer_manager (scheduler) == peer_manager);
150 : 1 : }
151 : :
152 : : /* Assert the signal emissions from #MwsScheduler are correct for a single call
153 : : * to mws_scheduler_update_entries().
154 : : *
155 : : * Two arrays of entries expected to be signalled as removed in a
156 : : * #MwsScheduler::active-entries-changed signal must be provided:
157 : : * * @expected_changed_active_removed1 for the entries signalled before
158 : : * #MwsScheduler::entries-changed is emitted
159 : : * * expected_changed_active_removed2 for the entries signalled afterwards
160 : : */
161 : : static void
162 : 79 : assert_entries_changed_signals (Fixture *fixture,
163 : : GPtrArray *expected_changed_added,
164 : : GPtrArray *expected_changed_removed,
165 : : GPtrArray *expected_changed_active_added,
166 : : GPtrArray *expected_changed_active_removed1,
167 : : GPtrArray *expected_changed_active_removed2)
168 : : {
169 : : /* Squash empty arrays. */
170 [ + + - + ]: 79 : if (expected_changed_added != NULL && expected_changed_added->len == 0)
171 : 0 : expected_changed_added = NULL;
172 [ + + - + ]: 79 : if (expected_changed_removed != NULL && expected_changed_removed->len == 0)
173 : 0 : expected_changed_removed = NULL;
174 [ + + + + ]: 79 : if (expected_changed_active_added != NULL && expected_changed_active_added->len == 0)
175 : 2 : expected_changed_active_added = NULL;
176 [ + + - + ]: 79 : if (expected_changed_active_removed1 != NULL && expected_changed_active_removed1->len == 0)
177 : 0 : expected_changed_active_removed1 = NULL;
178 [ + + - + ]: 79 : if (expected_changed_active_removed2 != NULL && expected_changed_active_removed2->len == 0)
179 : 0 : expected_changed_active_removed2 = NULL;
180 : :
181 : 79 : g_autoptr(GPtrArray) changed_added = NULL;
182 : 79 : g_autoptr(GPtrArray) changed_removed = NULL;
183 : 79 : g_autoptr(GPtrArray) changed_active_added1 = NULL;
184 : 79 : g_autoptr(GPtrArray) changed_active_removed1 = NULL;
185 : 79 : g_autoptr(GPtrArray) changed_active_added2 = NULL;
186 : 79 : g_autoptr(GPtrArray) changed_active_removed2 = NULL;
187 : :
188 : : /* We expect active-entries-changed to be emitted first for removed active
189 : : * entries (if there are any); then notify::entries and entries-changed; then
190 : : * active-entries-changed *again* for added active entries (if there are
191 : : * any). */
192 [ + + ]: 79 : if (expected_changed_active_removed1 != NULL)
193 [ + - + - : 62 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
194 : : fixture->scheduler, "active-entries-changed",
195 : : &changed_active_added1, &changed_active_removed1);
196 [ + + + + ]: 79 : if (expected_changed_added != NULL || expected_changed_removed != NULL)
197 : : {
198 [ + - + - : 110 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
199 : : fixture->scheduler, "notify::entries",
200 : : NULL);
201 [ + - + - : 110 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
202 : : fixture->scheduler, "entries-changed",
203 : : &changed_added, &changed_removed);
204 : : }
205 [ + + ]: 79 : if (expected_changed_active_added != NULL)
206 [ + - + - : 88 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
207 : : fixture->scheduler, "active-entries-changed",
208 : : &changed_active_added2, &changed_active_removed2);
209 : :
210 [ - + ]: 79 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
211 : :
212 : 79 : assert_ptr_arrays_equal (changed_added, expected_changed_added);
213 : 79 : assert_ptr_arrays_equal (changed_removed, expected_changed_removed);
214 : 79 : assert_ptr_arrays_equal (changed_active_added1, NULL);
215 : 79 : assert_ptr_arrays_equal (changed_active_removed1, expected_changed_active_removed1);
216 : 79 : assert_ptr_arrays_equal (changed_active_added2, expected_changed_active_added);
217 : 79 : assert_ptr_arrays_equal (changed_active_removed2, expected_changed_active_removed2);
218 : 79 : }
219 : :
220 : : /* Test that entries are added to and removed from the scheduler correctly. */
221 : : static void
222 : 1 : test_scheduler_entries (Fixture *fixture,
223 : : gconstpointer test_data)
224 : : {
225 : 1 : g_autoptr(GError) local_error = NULL;
226 : : gboolean success;
227 : : GHashTable *entries;
228 : :
229 : : /* Check that it can be a no-op. */
230 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, NULL, NULL, &local_error);
231 [ - + ]: 1 : g_assert_no_error (local_error);
232 [ - + ]: 1 : g_assert_true (success);
233 : :
234 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
235 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 0);
236 : :
237 [ - + ]: 1 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
238 : :
239 : : /* Add an entry. */
240 : 2 : g_autoptr(GPtrArray) added1 = g_ptr_array_new_with_free_func (g_object_unref);
241 : 1 : g_ptr_array_add (added1, mws_schedule_entry_new (":owner.1"));
242 : :
243 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, added1, NULL, &local_error);
244 [ - + ]: 1 : g_assert_no_error (local_error);
245 [ - + ]: 1 : g_assert_true (success);
246 : :
247 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
248 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 1);
249 : 1 : assert_entries_changed_signals (fixture, added1, NULL, added1, NULL, NULL);
250 : :
251 : 1 : MwsScheduleEntry *entry = mws_scheduler_get_entry (fixture->scheduler, "0");
252 [ - + ]: 1 : g_assert_nonnull (entry);
253 [ - + ]: 1 : g_assert_true (MWS_IS_SCHEDULE_ENTRY (entry));
254 [ - + ]: 1 : g_assert_true (mws_scheduler_is_entry_active (fixture->scheduler, entry));
255 : :
256 : : /* Remove an entry. */
257 : 2 : g_autoptr(GPtrArray) removed1 = g_ptr_array_new_with_free_func (NULL);
258 : 1 : g_ptr_array_add (removed1, mws_schedule_entry_get_id (entry));
259 : 2 : g_autoptr(GPtrArray) expected_removed1 = g_ptr_array_new_with_free_func (NULL);
260 : 1 : g_ptr_array_add (expected_removed1, entry);
261 : :
262 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, NULL, removed1, &local_error);
263 [ - + ]: 1 : g_assert_no_error (local_error);
264 [ - + ]: 1 : g_assert_true (success);
265 : :
266 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
267 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 0);
268 : 1 : assert_entries_changed_signals (fixture, NULL, expected_removed1, NULL, expected_removed1, NULL);
269 : :
270 : : /* Remove a non-existent entry. */
271 : 2 : g_autoptr(GPtrArray) removed2 = g_ptr_array_new_with_free_func (NULL);
272 : 1 : g_ptr_array_add (removed2, "nope");
273 : :
274 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, NULL, removed2, &local_error);
275 [ - + ]: 1 : g_assert_no_error (local_error);
276 [ - + ]: 1 : g_assert_true (success);
277 : :
278 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
279 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 0);
280 [ - + ]: 1 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
281 : :
282 : : /* Add several entries. */
283 : 2 : g_autoptr(GPtrArray) added2 = g_ptr_array_new_with_free_func (g_object_unref);
284 : 1 : g_ptr_array_add (added2, mws_schedule_entry_new (":owner.1"));
285 : 1 : g_ptr_array_add (added2, mws_schedule_entry_new (":owner.1"));
286 : 1 : g_ptr_array_add (added2, mws_schedule_entry_new (":owner.2"));
287 : :
288 : 2 : g_autoptr(GPtrArray) added_active2 = g_ptr_array_new_with_free_func (NULL);
289 : 1 : g_ptr_array_add (added_active2, added2->pdata[0]);
290 : :
291 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, added2, NULL, &local_error);
292 [ - + ]: 1 : g_assert_no_error (local_error);
293 [ - + ]: 1 : g_assert_true (success);
294 : :
295 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
296 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 3);
297 : 1 : assert_entries_changed_signals (fixture, added2, NULL, added_active2, NULL, NULL);
298 : :
299 : : /* Add duplicate entry. */
300 : 2 : g_autoptr(GPtrArray) added3 = g_ptr_array_new_with_free_func (NULL);
301 : 1 : g_ptr_array_add (added3, added2->pdata[0]);
302 : :
303 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, added3, NULL, &local_error);
304 [ - + ]: 1 : g_assert_no_error (local_error);
305 [ - + ]: 1 : g_assert_true (success);
306 : :
307 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
308 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 3);
309 [ - + ]: 1 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
310 : :
311 : : /* No-op when non-empty. */
312 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, NULL, NULL, &local_error);
313 [ - + ]: 1 : g_assert_no_error (local_error);
314 [ - + ]: 1 : g_assert_true (success);
315 : :
316 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
317 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 3);
318 [ - + ]: 1 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
319 : :
320 : : /* Remove several entries. */
321 : 2 : g_autoptr(GPtrArray) removed3 = g_ptr_array_new_with_free_func (NULL);
322 : 2 : g_autoptr(GPtrArray) expected_removed3 = g_ptr_array_new_with_free_func (NULL);
323 : :
324 : : GHashTableIter iter;
325 : : gpointer key, value;
326 : 1 : g_hash_table_iter_init (&iter, entries);
327 [ + + ]: 4 : while (g_hash_table_iter_next (&iter, &key, &value))
328 : : {
329 : 3 : g_ptr_array_add (removed3, key);
330 : 3 : g_ptr_array_add (expected_removed3, value);
331 : : }
332 : :
333 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, NULL, removed3, &local_error);
334 [ - + ]: 1 : g_assert_no_error (local_error);
335 [ - + ]: 1 : g_assert_true (success);
336 : :
337 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
338 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 0);
339 : 1 : assert_entries_changed_signals (fixture, NULL, expected_removed3, NULL, added_active2, NULL);
340 : 1 : }
341 : :
342 : : /* Test that entries can be removed by owner from a scheduler correctly. */
343 : : static void
344 : 1 : test_scheduler_entries_remove_for_owner (Fixture *fixture,
345 : : gconstpointer test_data)
346 : : {
347 : 1 : g_autoptr(GError) local_error = NULL;
348 : : gboolean success;
349 : : GHashTable *entries;
350 : :
351 : : /* Add several entries. */
352 : 2 : g_autoptr(GPtrArray) added1 = g_ptr_array_new_with_free_func (g_object_unref);
353 : 1 : g_ptr_array_add (added1, mws_schedule_entry_new (":owner.1"));
354 : 1 : g_ptr_array_add (added1, mws_schedule_entry_new (":owner.1"));
355 : 1 : g_ptr_array_add (added1, mws_schedule_entry_new (":owner.2"));
356 : :
357 : 2 : g_autoptr(GPtrArray) added_active1 = g_ptr_array_new_with_free_func (NULL);
358 : 1 : g_ptr_array_add (added_active1, added1->pdata[0]);
359 : :
360 : 1 : success = mws_scheduler_update_entries (fixture->scheduler, added1, NULL, &local_error);
361 [ - + ]: 1 : g_assert_no_error (local_error);
362 [ - + ]: 1 : g_assert_true (success);
363 : :
364 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
365 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 3);
366 : 1 : assert_entries_changed_signals (fixture, added1, NULL, added_active1, NULL, NULL);
367 : :
368 : : /* Remove all entries from one owner, including the active entry. */
369 : 1 : success = mws_scheduler_remove_entries_for_owner (fixture->scheduler, ":owner.1", &local_error);
370 [ - + ]: 1 : g_assert_no_error (local_error);
371 [ - + ]: 1 : g_assert_true (success);
372 : :
373 : 2 : g_autoptr(GPtrArray) removed1 = g_ptr_array_new_with_free_func (NULL);
374 : 1 : g_ptr_array_add (removed1, added1->pdata[0]);
375 : 1 : g_ptr_array_add (removed1, added1->pdata[1]);
376 : :
377 : 2 : g_autoptr(GPtrArray) removed_active1 = g_ptr_array_new_with_free_func (NULL);
378 : 1 : g_ptr_array_add (removed_active1, added1->pdata[0]);
379 : :
380 : 2 : g_autoptr(GPtrArray) added_active2 = g_ptr_array_new_with_free_func (NULL);
381 : 1 : g_ptr_array_add (added_active2, added1->pdata[2]);
382 : :
383 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
384 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 1);
385 : 1 : assert_entries_changed_signals (fixture, NULL, removed1, added_active2, added_active1, NULL);
386 : :
387 : : /* Remove entries from a non-existent owner. */
388 : 1 : success = mws_scheduler_remove_entries_for_owner (fixture->scheduler, ":owner.100", &local_error);
389 [ - + ]: 1 : g_assert_no_error (local_error);
390 [ - + ]: 1 : g_assert_true (success);
391 : :
392 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
393 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 1);
394 [ - + ]: 1 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
395 : :
396 : : /* Remove the remaining entries. */
397 : 1 : success = mws_scheduler_remove_entries_for_owner (fixture->scheduler, ":owner.2", &local_error);
398 [ - + ]: 1 : g_assert_no_error (local_error);
399 [ - + ]: 1 : g_assert_true (success);
400 : :
401 : 2 : g_autoptr(GPtrArray) removed2 = g_ptr_array_new_with_free_func (NULL);
402 : 1 : g_ptr_array_add (removed2, added1->pdata[2]);
403 : :
404 : 1 : entries = mws_scheduler_get_entries (fixture->scheduler);
405 [ - + ]: 1 : g_assert_cmpuint (g_hash_table_size (entries), ==, 0);
406 : 1 : assert_entries_changed_signals (fixture, NULL, removed2, NULL, added_active2, NULL);
407 : 1 : }
408 : :
409 : : /* Test that getting the properties from a #MwsScheduler works. */
410 : : static void
411 : 1 : test_scheduler_properties (Fixture *fixture,
412 : : gconstpointer test_data)
413 : : {
414 : 1 : g_autoptr(GHashTable) entries = NULL;
415 : : guint max_entries;
416 : 1 : g_autoptr(MwsConnectionMonitor) connection_monitor = NULL;
417 : : guint max_active_entries;
418 : 1 : g_autoptr(MwsPeerManager) peer_manager = NULL;
419 : : gboolean allow_downloads;
420 : 1 : g_autoptr(MwsClock) clock = NULL;
421 : :
422 : 1 : g_object_get (fixture->scheduler,
423 : : "entries", &entries,
424 : : "max-entries", &max_entries,
425 : : "connection-monitor", &connection_monitor,
426 : : "max-active-entries", &max_active_entries,
427 : : "peer-manager", &peer_manager,
428 : : "allow-downloads", &allow_downloads,
429 : : "clock", &clock,
430 : : NULL);
431 : :
432 [ - + ]: 1 : g_assert_nonnull (entries);
433 [ - + ]: 1 : g_assert_cmpuint (max_entries, >, 0);
434 [ - + ]: 1 : g_assert_nonnull (connection_monitor);
435 [ - + ]: 1 : g_assert_cmpuint (max_active_entries, >, 0);
436 [ - + ]: 1 : g_assert_nonnull (peer_manager);
437 [ - + ]: 1 : g_assert_nonnull (clock);
438 : 1 : }
439 : :
440 : : /* Convenience method to create a new schedule entry and set its priority. */
441 : : static MwsScheduleEntry *
442 : 19 : schedule_entry_new_with_priority (const gchar *owner,
443 : : guint32 priority)
444 : : {
445 : 38 : g_autoptr(MwsScheduleEntry) entry = mws_schedule_entry_new (owner);
446 : 19 : mws_schedule_entry_set_priority (entry, priority);
447 : 19 : return g_steal_pointer (&entry);
448 : : }
449 : :
450 : : /* Assert that when all the entries in @expected_scheduling_order are added to
451 : : * the scheduler, they are scheduled in the given order. This is checked by
452 : : * adding them all, checking which one entry is active, removing it, then
453 : : * checking which entry is made active next, etc. This checks all signal
454 : : * emissions. It requires the scheduler to be empty beforehand, and to have a
455 : : * max-active-entries limit of 1. */
456 : : static void
457 : 2 : assert_scheduling_order (Fixture *fixture,
458 : : const MwsScheduleEntry **expected_scheduling_order,
459 : : gsize n_entries)
460 : : {
461 : 2 : g_autoptr(GError) local_error = NULL;
462 : :
463 [ - + ]: 2 : g_assert (n_entries > 0);
464 [ - + ]: 2 : g_assert (g_hash_table_size (mws_scheduler_get_entries (fixture->scheduler)) == 0);
465 : : guint max_active_entries;
466 : 2 : g_object_get (fixture->scheduler, "max-active-entries", &max_active_entries, NULL);
467 [ - + ]: 2 : g_assert (max_active_entries == 1);
468 : :
469 : : /* Add all the entries to the scheduler. Add them in the reverse of the
470 : : * expected scheduling order, just in case the scheduler is being really dumb. */
471 : 4 : g_autoptr(GPtrArray) added = g_ptr_array_new_with_free_func (NULL);
472 [ + + ]: 14 : for (gsize i = 0; i < n_entries; i++)
473 : 12 : g_ptr_array_add (added, expected_scheduling_order[n_entries - 1 - i]);
474 : :
475 : 4 : g_autoptr(GPtrArray) expected_active = g_ptr_array_new_with_free_func (NULL);
476 : 2 : g_ptr_array_add (expected_active, expected_scheduling_order[0]);
477 : :
478 : 2 : mws_scheduler_update_entries (fixture->scheduler, added, NULL, &local_error);
479 [ - + ]: 2 : g_assert_no_error (local_error);
480 : 2 : assert_entries_changed_signals (fixture, added, NULL, expected_active, NULL, NULL);
481 : :
482 : : /* Remove each active entry to work out what is going to be scheduled next. */
483 [ + + ]: 14 : for (gsize i = 0; i < n_entries; i++)
484 : : {
485 : 24 : g_autoptr(GPtrArray) removed = g_ptr_array_new_with_free_func (NULL);
486 : 12 : g_ptr_array_add (removed, mws_schedule_entry_get_id (expected_scheduling_order[i]));
487 : :
488 : 24 : g_autoptr(GPtrArray) expected_removed = g_ptr_array_new_with_free_func (NULL);
489 : 12 : g_ptr_array_add (expected_removed, expected_scheduling_order[i]);
490 : :
491 : 24 : g_autoptr(GPtrArray) old_expected_active = g_steal_pointer (&expected_active);
492 : 12 : expected_active = g_ptr_array_new_with_free_func (NULL);
493 [ + + ]: 12 : if (i + 1 < n_entries)
494 : 10 : g_ptr_array_add (expected_active, expected_scheduling_order[i + 1]);
495 : :
496 : 12 : mws_scheduler_update_entries (fixture->scheduler, NULL, removed, &local_error);
497 [ - + ]: 12 : g_assert_no_error (local_error);
498 : 12 : assert_entries_changed_signals (fixture,
499 : : NULL, expected_removed,
500 : : expected_active, old_expected_active, NULL);
501 : : }
502 : 2 : }
503 : :
504 : : /* Test that schedule entries are correctly ordered by their priority within a
505 : : * single peer. */
506 : : static void
507 : 1 : test_scheduler_scheduling_entry_priorities (Fixture *fixture,
508 : : gconstpointer test_data)
509 : : {
510 : : /* Add several entries. @entry4 and @entry5 have the same priority, but ties
511 : : * are broken using the entry ID, so @entry5 loses as its ID is slightly
512 : : * higher. */
513 : 2 : g_autoptr(MwsScheduleEntry) entry1 = schedule_entry_new_with_priority (":owner.1", 5);
514 : 2 : g_autoptr(MwsScheduleEntry) entry2 = schedule_entry_new_with_priority (":owner.1", 10);
515 : 2 : g_autoptr(MwsScheduleEntry) entry3 = schedule_entry_new_with_priority (":owner.1", 15);
516 : 2 : g_autoptr(MwsScheduleEntry) entry4 = schedule_entry_new_with_priority (":owner.1", 16);
517 : 2 : g_autoptr(MwsScheduleEntry) entry5 = schedule_entry_new_with_priority (":owner.1", 16);
518 : :
519 : : /* We expect @entry4 to be scheduled first, then @entry5, etc. */
520 : 1 : const MwsScheduleEntry *expected_scheduling_order[] = { entry4, entry5, entry3, entry2, entry1 };
521 : :
522 : 1 : assert_scheduling_order (fixture, expected_scheduling_order,
523 : : G_N_ELEMENTS (expected_scheduling_order));
524 : 1 : }
525 : :
526 : : /* Test that schedule entries are correctly ordered by their priority between
527 : : * multiple peers. */
528 : : static void
529 : 1 : test_scheduler_scheduling_peer_priorities (Fixture *fixture,
530 : : gconstpointer test_data)
531 : : {
532 : : /* Add several entries. */
533 : 2 : g_autoptr(MwsScheduleEntry) entry1 = schedule_entry_new_with_priority (":eos.updater", 5);
534 : 2 : g_autoptr(MwsScheduleEntry) entry2 = schedule_entry_new_with_priority (":gnome.software", 10);
535 : 2 : g_autoptr(MwsScheduleEntry) entry3 = schedule_entry_new_with_priority (":eos.updater", 15);
536 : 2 : g_autoptr(MwsScheduleEntry) entry4 = schedule_entry_new_with_priority (":random.program.1", 12);
537 : 2 : g_autoptr(MwsScheduleEntry) entry5 = schedule_entry_new_with_priority (":random.program.1", 100);
538 : 2 : g_autoptr(MwsScheduleEntry) entry6 = schedule_entry_new_with_priority (":random.program.2", 2);
539 : 2 : g_autoptr(MwsScheduleEntry) entry7 = schedule_entry_new_with_priority (":unknown.peer", 110);
540 : :
541 : : /* Set up the peer credentials. */
542 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
543 : : ":eos.updater", "/usr/libexec/eos-updater");
544 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
545 : : ":gnome.software", "/usr/bin/gnome-software");
546 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
547 : : ":random.program.1", "/some/random/path1");
548 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
549 : : ":random.program.2", "/some/random/path2");
550 : :
551 : : /* We expect @entry3 to be scheduled first, then @entry2, etc. Note that since
552 : : * the priority order of :random.program.1 and :random.program.2 is undefined
553 : : * (depends on the hash of their unique names, so may change between GLib
554 : : * versions), [entry6] and [entry5, entry4] may change places at some point
555 : : * in the future. */
556 : 1 : const MwsScheduleEntry *expected_scheduling_order[] =
557 : : { entry3, entry2, entry1, entry6, entry5, entry4, entry7 };
558 : :
559 : 1 : assert_scheduling_order (fixture, expected_scheduling_order,
560 : : G_N_ELEMENTS (expected_scheduling_order));
561 : 1 : }
562 : :
563 : : /* Test that the size of the set of active entries is limited by
564 : : * #MwsScheduler:max-active-entries, and that only elements in that set are
565 : : * mentioned in #MwsScheduler::active-entries-changed signals.
566 : : *
567 : : * Schedule two entries, then add two more with priorities such that one of them
568 : : * will be scheduled in place of one of the original entries. Remove one of the
569 : : * scheduled entries so one of the unscheduled entries is scheduled; then remove
570 : : * the remaining unscheduled entry to check that it’s not mentioned in
571 : : * ::active-entries-changed signals. */
572 : : static void
573 : 1 : test_scheduler_scheduling_max_active_entries (Fixture *fixture,
574 : : gconstpointer test_data)
575 : : {
576 : 1 : const TestData *data = test_data;
577 [ - + ]: 1 : g_assert (data->max_active_entries == 2);
578 : :
579 : 1 : g_autoptr(GError) local_error = NULL;
580 : :
581 : : /* Add several entries. */
582 : 2 : g_autoptr(MwsScheduleEntry) entry1 = schedule_entry_new_with_priority (":owner.1", 5);
583 : 2 : g_autoptr(MwsScheduleEntry) entry2 = schedule_entry_new_with_priority (":owner.1", 10);
584 : :
585 : : /* Set up the peer credentials. */
586 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
587 : : ":owner.1", "/some/owner");
588 : :
589 : : /* Add all the entries to the scheduler. */
590 : 2 : g_autoptr(GPtrArray) added1 = g_ptr_array_new_with_free_func (NULL);
591 : 1 : g_ptr_array_add (added1, entry1);
592 : 1 : g_ptr_array_add (added1, entry2);
593 : :
594 : 2 : g_autoptr(GPtrArray) expected_active1 = g_ptr_array_new_with_free_func (NULL);
595 : 1 : g_ptr_array_add (expected_active1, entry2);
596 : 1 : g_ptr_array_add (expected_active1, entry1);
597 : :
598 : 1 : mws_scheduler_update_entries (fixture->scheduler, added1, NULL, &local_error);
599 [ - + ]: 1 : g_assert_no_error (local_error);
600 : 1 : assert_entries_changed_signals (fixture, added1, NULL, expected_active1, NULL, NULL);
601 : :
602 : : /* Add some more entries, one of which at a higher priority so that one of the
603 : : * old ones is descheduled. */
604 : 2 : g_autoptr(MwsScheduleEntry) entry3 = schedule_entry_new_with_priority (":owner.1", 15);
605 : 2 : g_autoptr(MwsScheduleEntry) entry4 = schedule_entry_new_with_priority (":owner.1", 7);
606 : :
607 : 2 : g_autoptr(GPtrArray) added2 = g_ptr_array_new_with_free_func (NULL);
608 : 1 : g_ptr_array_add (added2, entry3);
609 : 1 : g_ptr_array_add (added2, entry4);
610 : :
611 : 2 : g_autoptr(GPtrArray) expected_active2 = g_ptr_array_new_with_free_func (NULL);
612 : 1 : g_ptr_array_add (expected_active2, entry3);
613 : :
614 : 2 : g_autoptr(GPtrArray) expected_inactive1 = g_ptr_array_new_with_free_func (NULL);
615 : 1 : g_ptr_array_add (expected_inactive1, entry1);
616 : :
617 : 1 : mws_scheduler_update_entries (fixture->scheduler, added2, NULL, &local_error);
618 [ - + ]: 1 : g_assert_no_error (local_error);
619 : 1 : assert_entries_changed_signals (fixture, added2, NULL, expected_active2, NULL, expected_inactive1);
620 : :
621 : : /* Remove one of the high priority entries so another should be scheduled in
622 : : * its place. */
623 : 2 : g_autoptr(GPtrArray) removed_ids1 = g_ptr_array_new_with_free_func (NULL);
624 : 1 : g_ptr_array_add (removed_ids1, mws_schedule_entry_get_id (entry3));
625 : 2 : g_autoptr(GPtrArray) removed1 = g_ptr_array_new_with_free_func (NULL);
626 : 1 : g_ptr_array_add (removed1, entry3);
627 : :
628 : 2 : g_autoptr(GPtrArray) expected_active3 = g_ptr_array_new_with_free_func (NULL);
629 : 1 : g_ptr_array_add (expected_active3, entry4);
630 : :
631 : 2 : g_autoptr(GPtrArray) expected_inactive2 = g_ptr_array_new_with_free_func (NULL);
632 : 1 : g_ptr_array_add (expected_inactive2, entry3);
633 : :
634 : 1 : mws_scheduler_update_entries (fixture->scheduler, NULL, removed_ids1, &local_error);
635 [ - + ]: 1 : g_assert_no_error (local_error);
636 : 1 : assert_entries_changed_signals (fixture, NULL, removed1, expected_active3, expected_inactive2, NULL);
637 : :
638 : : /* Now remove an inactive entry and check it’s not signalled. */
639 : 2 : g_autoptr(GPtrArray) removed_ids2 = g_ptr_array_new_with_free_func (NULL);
640 : 1 : g_ptr_array_add (removed_ids2, mws_schedule_entry_get_id (entry1));
641 : 2 : g_autoptr(GPtrArray) removed2 = g_ptr_array_new_with_free_func (NULL);
642 : 1 : g_ptr_array_add (removed2, entry1);
643 : :
644 : 1 : mws_scheduler_update_entries (fixture->scheduler, NULL, removed_ids2, &local_error);
645 [ - + ]: 1 : g_assert_no_error (local_error);
646 : 1 : assert_entries_changed_signals (fixture, NULL, removed2, NULL, NULL, NULL);
647 : 1 : }
648 : :
649 : : /* Test that the schedule entries for a given peer are automatically removed if
650 : : * that peer vanishes, whether they are active or not. */
651 : : static void
652 : 1 : test_scheduler_scheduling_peer_vanished (Fixture *fixture,
653 : : gconstpointer test_data)
654 : : {
655 : 1 : g_autoptr(GError) local_error = NULL;
656 : :
657 : : /* Add several entries. */
658 : 2 : g_autoptr(MwsScheduleEntry) entry1 = schedule_entry_new_with_priority (":owner.1", 5);
659 : 2 : g_autoptr(MwsScheduleEntry) entry2 = schedule_entry_new_with_priority (":owner.1", 10);
660 : 2 : g_autoptr(MwsScheduleEntry) entry3 = schedule_entry_new_with_priority (":owner.2", 2);
661 : :
662 : : /* Set up the peer credentials. Make sure :owner.1 is scheduled first, by
663 : : * making it gnome-software. */
664 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
665 : : ":owner.1", "/usr/bin/gnome-software");
666 : 1 : mws_peer_manager_dummy_set_peer_credentials (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
667 : : ":owner.2", "/some/other/path");
668 : :
669 : : /* Add all the entries to the scheduler. */
670 : 2 : g_autoptr(GPtrArray) added = g_ptr_array_new_with_free_func (NULL);
671 : 1 : g_ptr_array_add (added, entry1);
672 : 1 : g_ptr_array_add (added, entry2);
673 : 1 : g_ptr_array_add (added, entry3);
674 : :
675 : 2 : g_autoptr(GPtrArray) expected_active1 = g_ptr_array_new_with_free_func (NULL);
676 : 1 : g_ptr_array_add (expected_active1, entry2);
677 : :
678 : 1 : mws_scheduler_update_entries (fixture->scheduler, added, NULL, &local_error);
679 [ - + ]: 1 : g_assert_no_error (local_error);
680 : 1 : assert_entries_changed_signals (fixture, added, NULL, expected_active1, NULL, NULL);
681 : :
682 : : /* Make `:owner.1` vanish, and check that both its entries (but not the third
683 : : * entry) disappear. */
684 : 1 : mws_peer_manager_dummy_remove_peer (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
685 : : ":owner.1");
686 : :
687 : 2 : g_autoptr(GPtrArray) expected_removed = g_ptr_array_new_with_free_func (NULL);
688 : 1 : g_ptr_array_add (expected_removed, entry2);
689 : 1 : g_ptr_array_add (expected_removed, entry1);
690 : :
691 : 2 : g_autoptr(GPtrArray) expected_active2 = g_ptr_array_new_with_free_func (NULL);
692 : 1 : g_ptr_array_add (expected_active2, entry3);
693 : :
694 : 1 : assert_entries_changed_signals (fixture, NULL, expected_removed,
695 : : expected_active2, expected_active1, NULL);
696 : :
697 : : /* Now make `:owner.2` vanish, to test what happens when there will be no new
698 : : * active entry to take the place of the one being removed. */
699 : 1 : mws_peer_manager_dummy_remove_peer (MWS_PEER_MANAGER_DUMMY (fixture->peer_manager),
700 : : ":owner.2");
701 : :
702 : 1 : assert_entries_changed_signals (fixture, NULL, expected_active2, NULL, expected_active2, NULL);
703 : 1 : }
704 : :
705 : : /* Test the transitions between different network connection states, checking
706 : : * whether they cause a single entry to be scheduled/unscheduled
707 : : * appropriately. Each test vector provides two states of a set of network
708 : : * connections. The test starts in state 1, checks whether the entry is
709 : : * scheduled appropriately, transitions to state 2, checks again, and then
710 : : * transitions back to state 1 to test the reverse transition (and checks
711 : : * again). */
712 : : static void
713 : 1 : test_scheduler_scheduling_metered_connection (Fixture *fixture,
714 : : gconstpointer test_data)
715 : : {
716 : : /* Various pre-defined connection details, which can be assigned for use below. */
717 : 1 : const MwsConnectionDetails connection_metered =
718 : : {
719 : : .metered = MWS_METERED_YES,
720 : : .allow_downloads_when_metered = FALSE,
721 : : .allow_downloads = TRUE,
722 : : .tariff = NULL,
723 : : };
724 : 1 : const MwsConnectionDetails connection_maybe_metered =
725 : : {
726 : : .metered = MWS_METERED_GUESS_YES,
727 : : .allow_downloads_when_metered = FALSE,
728 : : .allow_downloads = TRUE,
729 : : .tariff = NULL,
730 : : };
731 : 1 : const MwsConnectionDetails connection_unmetered =
732 : : {
733 : : .metered = MWS_METERED_NO,
734 : : .allow_downloads_when_metered = FALSE,
735 : : .allow_downloads = TRUE,
736 : : .tariff = NULL,
737 : : };
738 : 1 : const MwsConnectionDetails connection_metered_allow_downloads =
739 : : {
740 : : .metered = MWS_METERED_YES,
741 : : .allow_downloads_when_metered = TRUE,
742 : : .allow_downloads = TRUE,
743 : : .tariff = NULL,
744 : : };
745 : 1 : const MwsConnectionDetails connection_metered_no_downloads =
746 : : {
747 : : .metered = MWS_METERED_YES,
748 : : .allow_downloads_when_metered = FALSE,
749 : : .allow_downloads = FALSE,
750 : : .tariff = NULL,
751 : : };
752 : 1 : const MwsConnectionDetails connection_unmetered_no_downloads =
753 : : {
754 : : .metered = MWS_METERED_NO,
755 : : .allow_downloads_when_metered = FALSE,
756 : : .allow_downloads = FALSE,
757 : : .tariff = NULL,
758 : : };
759 : :
760 : 1 : g_autoptr(GError) local_error = NULL;
761 : : struct
762 : : {
763 : : /* We use a fixed limit of 3 connections in these tests, because we should
764 : : * be able to simulate any condition we care about using 3. Typically,
765 : : * systems will have only 1 active connection (maybe 2 if they have wired
766 : : * and Wi-Fi enabled at the same time). */
767 : : const MwsConnectionDetails *state1_connections[3];
768 : : gboolean state1_expected_allow_downloads;
769 : : gboolean state1_expected_active;
770 : : const MwsConnectionDetails *state2_connections[3];
771 : : gboolean state2_expected_allow_downloads;
772 : : gboolean state2_expected_active;
773 : : }
774 : 1 : transitions[] =
775 : : {
776 : : /* Transition from definitely metered to definitely unmetered. */
777 : : { { &connection_metered, }, TRUE, FALSE,
778 : : { &connection_unmetered, }, TRUE, TRUE },
779 : : /* Transition from maybe metered to definitely unmetered. */
780 : : { { &connection_maybe_metered, }, TRUE, FALSE,
781 : : { &connection_unmetered, }, TRUE, TRUE },
782 : : /* Transition from maybe metered to definitely unmetered. */
783 : : { { &connection_maybe_metered, }, TRUE, FALSE,
784 : : { &connection_unmetered, }, TRUE, TRUE },
785 : : /* Transition from definitely metered to definitely unmetered, but with
786 : : * downloads disabled. */
787 : : { { &connection_metered_no_downloads, }, FALSE, FALSE,
788 : : { &connection_unmetered_no_downloads, }, FALSE, FALSE },
789 : : /* Transition from unmetered to metered (but with downloads allowed). */
790 : : { { &connection_unmetered, }, TRUE, TRUE,
791 : : { &connection_metered_allow_downloads, }, TRUE, TRUE },
792 : : /* Transition from metered to metered (but with downloads allowed). */
793 : : { { &connection_metered, }, TRUE, FALSE,
794 : : { &connection_metered_allow_downloads, }, TRUE, TRUE },
795 : : /* Transition from two definitely metered connections to having both of
796 : : * them definitely unmetered. */
797 : : { { &connection_metered, &connection_metered }, TRUE, FALSE,
798 : : { &connection_unmetered, &connection_unmetered, }, TRUE, TRUE },
799 : : /* Transition from two definitely metered connections to having one of
800 : : * them definitely unmetered. */
801 : : { { &connection_metered, &connection_metered }, TRUE, FALSE,
802 : : { &connection_metered, &connection_unmetered, }, TRUE, FALSE },
803 : : /* Transition from a metered and an unmetered connection to having one of
804 : : * them allow downloads. */
805 : : { { &connection_unmetered, &connection_metered }, TRUE, FALSE,
806 : : { &connection_unmetered, &connection_metered_allow_downloads, }, TRUE, TRUE },
807 : : /* Transition from a selection of connections to various unmetered ones
808 : : * with downloads disallowed. allow-downloads must become false. */
809 : : { { &connection_unmetered, &connection_metered, &connection_unmetered }, TRUE, FALSE,
810 : : { &connection_unmetered_no_downloads, &connection_unmetered_no_downloads, &connection_unmetered_no_downloads }, FALSE, FALSE },
811 : : };
812 : :
813 [ + + ]: 11 : for (gsize i = 0; i < G_N_ELEMENTS (transitions); i++)
814 : : {
815 : : /* We only care about transitions within a given connection in this test,
816 : : * so the set of connections available in states 1 and 2 must be the same */
817 [ + + ]: 40 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_connections); j++)
818 [ - + ]: 30 : g_assert ((transitions[i].state1_connections[j] == NULL) ==
819 : : (transitions[i].state2_connections[j] == NULL));
820 : :
821 : 10 : g_test_message ("Transition test %" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT,
822 : : i + 1, G_N_ELEMENTS (transitions));
823 : :
824 : 10 : gboolean initial_allow_downloads = mws_scheduler_get_allow_downloads (fixture->scheduler);
825 : :
826 : : /* Set up the connections in state 1. */
827 : 10 : g_autoptr(GHashTable) state1_connections =
828 : 10 : g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
829 : :
830 [ + + ]: 40 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_connections); j++)
831 : : {
832 [ + + ]: 30 : if (transitions[i].state1_connections[j] != NULL)
833 : : {
834 : 30 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
835 : 15 : g_hash_table_insert (state1_connections, g_steal_pointer (&id),
836 : 15 : transitions[i].state1_connections[j]);
837 : : }
838 : : }
839 : :
840 : 10 : mws_connection_monitor_dummy_update_connections (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
841 : : state1_connections, NULL);
842 : :
843 [ + + ]: 10 : if (transitions[i].state1_expected_allow_downloads != initial_allow_downloads)
844 [ + - + - : 2 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
845 : : fixture->scheduler,
846 : : "notify::allow-downloads", NULL);
847 [ - + ]: 10 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
848 : :
849 : : /* Add a single entry to the scheduler. */
850 : 20 : g_autoptr(MwsScheduleEntry) entry = mws_schedule_entry_new (":owner.1");
851 : :
852 : 20 : g_autoptr(GPtrArray) entry_array = g_ptr_array_new_with_free_func (NULL);
853 : 10 : g_ptr_array_add (entry_array, entry);
854 : :
855 : 10 : mws_scheduler_update_entries (fixture->scheduler, entry_array, NULL, &local_error);
856 [ - + ]: 10 : g_assert_no_error (local_error);
857 : 10 : assert_entries_changed_signals (fixture, entry_array, NULL,
858 [ + + ]: 10 : (transitions[i].state1_expected_active ? entry_array : NULL),
859 : : NULL, NULL);
860 : :
861 : : /* Change to the next connection state. */
862 [ + + ]: 40 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state2_connections); j++)
863 : : {
864 [ + + ]: 30 : if (transitions[i].state2_connections[j] != NULL)
865 : : {
866 : 30 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
867 : 15 : mws_connection_monitor_dummy_update_connection (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
868 : : id, transitions[i].state2_connections[j]);
869 : : }
870 : : }
871 : :
872 [ + + ]: 10 : if (transitions[i].state2_expected_allow_downloads != transitions[i].state1_expected_allow_downloads)
873 [ + - + - : 2 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
874 : : fixture->scheduler,
875 : : "notify::allow-downloads", NULL);
876 : :
877 [ + + ]: 10 : if (transitions[i].state1_expected_active == transitions[i].state2_expected_active)
878 [ - + ]: 4 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
879 [ - + ]: 6 : else if (transitions[i].state1_expected_active)
880 : 0 : assert_entries_changed_signals (fixture, NULL, NULL, NULL, entry_array, NULL);
881 : : else
882 : 6 : assert_entries_changed_signals (fixture, NULL, NULL, entry_array, NULL, NULL);
883 : :
884 : : /* Change back to the previous connection state. The entry’s scheduled
885 : : * state should always be the same as before. */
886 [ + + ]: 40 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_connections); j++)
887 : : {
888 [ + + ]: 30 : if (transitions[i].state1_connections[j] != NULL)
889 : : {
890 : 30 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
891 : 15 : mws_connection_monitor_dummy_update_connection (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
892 : : id, transitions[i].state1_connections[j]);
893 : : }
894 : : }
895 : :
896 [ + + ]: 10 : if (transitions[i].state1_expected_allow_downloads != transitions[i].state2_expected_allow_downloads)
897 [ + - + - : 2 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
+ - ]
898 : : fixture->scheduler,
899 : : "notify::allow-downloads", NULL);
900 : :
901 [ + + ]: 10 : if (transitions[i].state1_expected_active == transitions[i].state2_expected_active)
902 [ - + ]: 4 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
903 [ + - ]: 6 : else if (transitions[i].state2_expected_active)
904 : 6 : assert_entries_changed_signals (fixture, NULL, NULL, NULL, entry_array, NULL);
905 : : else
906 : 0 : assert_entries_changed_signals (fixture, NULL, NULL, entry_array, NULL, NULL);
907 : :
908 : : /* Clean up. */
909 : 10 : teardown (fixture, test_data);
910 : 10 : setup (fixture, test_data);
911 : : }
912 : 1 : }
913 : :
914 : : static GTimeZone *
915 : 34 : time_zone_new (const gchar *tz_str)
916 : : {
917 [ + + ]: 34 : if (tz_str != NULL)
918 : : {
919 : : #if GLIB_CHECK_VERSION(2, 68, 0)
920 : 14 : g_autoptr(GTimeZone) tz = g_time_zone_new_identifier (tz_str);
921 [ - + ]: 14 : g_assert_nonnull (tz);
922 : 14 : return g_steal_pointer (&tz);
923 : : #else
924 : : return g_time_zone_new (tz_str);
925 : : #endif
926 : : }
927 : : else
928 : : {
929 : 20 : return g_time_zone_new_utc ();
930 : : }
931 : : }
932 : :
933 : : /* Test the transitions between different tariffs, checking whether they cause a
934 : : * single entry to be scheduled/unscheduled appropriately. Each test vector
935 : : * provides two states of a set of tariffs, and two times. Either the set of
936 : : * tariffs can vary, or the time can vary in a single vector — not both. The
937 : : * test starts in state 1 at time 1, checks whether the entry is scheduled
938 : : * appropriately, transitions to state 2 at time 2, checks again, and then
939 : : * transitions back to state 1 at time 1 to test the reverse transition (and
940 : : * checks again). */
941 : : static void
942 : 1 : test_scheduler_scheduling_tariff (Fixture *fixture,
943 : : gconstpointer test_data)
944 : : {
945 : : /* A tariff which is normally unmetered, but has a period from 01:00–02:00
946 : : * each day which has zero capacity limit. */
947 : 2 : g_autoptr(GPtrArray) tariff1_periods = g_ptr_array_new_with_free_func (NULL);
948 : :
949 : 2 : g_autoptr(GDateTime) tariff1_period1_start = g_date_time_new_utc (2018, 1, 1, 0, 0, 0);
950 : 2 : g_autoptr(GDateTime) tariff1_period1_end = g_date_time_new_utc (2018, 1, 2, 0, 0, 0);
951 : 2 : g_autoptr(MwtPeriod) tariff1_period1 = mwt_period_new (tariff1_period1_start,
952 : : tariff1_period1_end,
953 : : MWT_PERIOD_REPEAT_DAY,
954 : : 1,
955 : : "capacity-limit", G_MAXUINT64,
956 : : NULL);
957 : 1 : g_ptr_array_add (tariff1_periods, tariff1_period1);
958 : :
959 : 2 : g_autoptr(GDateTime) tariff1_period2_start = g_date_time_new_utc (2018, 1, 1, 1, 0, 0);
960 : 2 : g_autoptr(GDateTime) tariff1_period2_end = g_date_time_new_utc (2018, 1, 1, 2, 0, 0);
961 : 2 : g_autoptr(MwtPeriod) tariff1_period2 = mwt_period_new (tariff1_period2_start,
962 : : tariff1_period2_end,
963 : : MWT_PERIOD_REPEAT_DAY,
964 : : 1,
965 : : "capacity-limit", G_GUINT64_CONSTANT (0),
966 : : NULL);
967 : 1 : g_ptr_array_add (tariff1_periods, tariff1_period2);
968 : :
969 : 2 : g_autoptr(MwtTariff) tariff1 = mwt_tariff_new ("tariff1", tariff1_periods);
970 : :
971 : : /* A tariff which is always unmetered (infinite capacity limit). */
972 : 2 : g_autoptr(GPtrArray) tariff_unmetered_periods = g_ptr_array_new_with_free_func (NULL);
973 : :
974 : 2 : g_autoptr(GDateTime) tariff_unmetered_period1_start = g_date_time_new_utc (2018, 1, 1, 0, 0, 0);
975 : 2 : g_autoptr(GDateTime) tariff_unmetered_period1_end = g_date_time_new_utc (2018, 1, 2, 0, 0, 0);
976 : 2 : g_autoptr(MwtPeriod) tariff_unmetered_period1 = mwt_period_new (tariff_unmetered_period1_start,
977 : : tariff_unmetered_period1_end,
978 : : MWT_PERIOD_REPEAT_DAY,
979 : : 1,
980 : : "capacity-limit", G_MAXUINT64,
981 : : NULL);
982 : 1 : g_ptr_array_add (tariff_unmetered_periods, tariff_unmetered_period1);
983 : :
984 : 2 : g_autoptr(MwtTariff) tariff_unmetered = mwt_tariff_new ("tariff_unmetered",
985 : : tariff_unmetered_periods);
986 : :
987 : : /* A tariff which is normally metered, but has a period from 01:30–02:00
988 : : * each day which has unlimited capacity. */
989 : 2 : g_autoptr(GPtrArray) tariff2_periods = g_ptr_array_new_with_free_func (NULL);
990 : :
991 : 2 : g_autoptr(GDateTime) tariff2_period1_start = g_date_time_new_utc (2018, 1, 1, 0, 0, 0);
992 : 2 : g_autoptr(GDateTime) tariff2_period1_end = g_date_time_new_utc (2018, 1, 2, 0, 0, 0);
993 : 2 : g_autoptr(MwtPeriod) tariff2_period1 = mwt_period_new (tariff2_period1_start,
994 : : tariff2_period1_end,
995 : : MWT_PERIOD_REPEAT_DAY,
996 : : 1,
997 : : "capacity-limit", G_GUINT64_CONSTANT (0),
998 : : NULL);
999 : 1 : g_ptr_array_add (tariff2_periods, tariff2_period1);
1000 : :
1001 : 2 : g_autoptr(GDateTime) tariff2_period2_start = g_date_time_new_utc (2018, 1, 1, 1, 30, 0);
1002 : 2 : g_autoptr(GDateTime) tariff2_period2_end = g_date_time_new_utc (2018, 1, 1, 2, 30, 0);
1003 : 2 : g_autoptr(MwtPeriod) tariff2_period2 = mwt_period_new (tariff2_period2_start,
1004 : : tariff2_period2_end,
1005 : : MWT_PERIOD_REPEAT_DAY,
1006 : : 1,
1007 : : "capacity-limit", G_MAXUINT64,
1008 : : NULL);
1009 : 1 : g_ptr_array_add (tariff2_periods, tariff2_period2);
1010 : :
1011 : 2 : g_autoptr(MwtTariff) tariff2 = mwt_tariff_new ("tariff2", tariff2_periods);
1012 : :
1013 : : /* A tariff has a single period and no recurrence. */
1014 : 2 : g_autoptr(GPtrArray) tariff3_periods = g_ptr_array_new_with_free_func (NULL);
1015 : :
1016 : 2 : g_autoptr(GDateTime) tariff3_period1_start = g_date_time_new_utc (2018, 1, 1, 0, 0, 0);
1017 : 2 : g_autoptr(GDateTime) tariff3_period1_end = g_date_time_new_utc (2018, 1, 2, 0, 0, 0);
1018 : 2 : g_autoptr(MwtPeriod) tariff3_period1 = mwt_period_new (tariff3_period1_start,
1019 : : tariff3_period1_end,
1020 : : MWT_PERIOD_REPEAT_NONE,
1021 : : 0,
1022 : : "capacity-limit", G_GUINT64_CONSTANT (0),
1023 : : NULL);
1024 : 1 : g_ptr_array_add (tariff3_periods, tariff3_period1);
1025 : :
1026 : 2 : g_autoptr(MwtTariff) tariff3 = mwt_tariff_new ("tariff3", tariff3_periods);
1027 : :
1028 : 1 : g_autoptr(GError) local_error = NULL;
1029 : : struct
1030 : : {
1031 : : /* We use a fixed limit of 3 tariffs in these tests, because we should
1032 : : * be able to simulate any condition we care about using 3. Typically,
1033 : : * systems will have only 1 active connection (maybe 2 if they have wired
1034 : : * and Wi-Fi enabled at the same time). Each tariff listed here will
1035 : : * result in a separate network connection being configured.
1036 : : *
1037 : : * The dates/times below are given in UTC, and then converted to the
1038 : : * timezone given as @state1_tz or @state2_tz. This means they represent
1039 : : * the same instant as the UTC time, and hence @state1_expected_active
1040 : : * and @state2_expected_active are independent of @state1_tz and
1041 : : * @state2_tz. */
1042 : : const MwtTariff *state1_tariffs[3];
1043 : : gint state1_year;
1044 : : gint state1_month;
1045 : : gint state1_day;
1046 : : gint state1_hour;
1047 : : gint state1_minute;
1048 : : gdouble state1_seconds;
1049 : : const gchar *state1_tz;
1050 : : gboolean state1_expected_active;
1051 : : const MwtTariff *state2_tariffs[3];
1052 : : gint state2_year;
1053 : : gint state2_month;
1054 : : gint state2_day;
1055 : : gint state2_hour;
1056 : : gint state2_minute;
1057 : : gdouble state2_seconds;
1058 : : const gchar *state2_tz;
1059 : : gboolean state2_expected_active;
1060 : : }
1061 : 1 : transitions[] =
1062 : : {
1063 : : /* Transition from an unmetered period to a metered period in the same
1064 : : * tariff. */
1065 : : { { tariff1, }, 2018, 2, 3, 17, 0, 0, NULL, TRUE,
1066 : : { tariff1, }, 2018, 2, 4, 1, 30, 0, NULL, FALSE },
1067 : : /* Transition within an unmetered period, and within a metered period. */
1068 : : { { tariff1, }, 2018, 2, 3, 17, 0, 0, NULL, TRUE,
1069 : : { tariff1, }, 2018, 2, 3, 17, 30, 0, NULL, TRUE },
1070 : : { { tariff1, }, 2018, 2, 4, 1, 15, 0, NULL, FALSE,
1071 : : { tariff1, }, 2018, 2, 4, 1, 30, 0, NULL, FALSE },
1072 : : /* Transition between an unmetered tariff and a normal tariff, for a time
1073 : : * which is and is not in the tariff metered period. */
1074 : : { { tariff_unmetered, }, 2018, 2, 3, 17, 0, 0, NULL, TRUE,
1075 : : { tariff1, }, 2018, 2, 3, 17, 0, 0, NULL, TRUE },
1076 : : { { tariff_unmetered, }, 2018, 2, 4, 1, 30, 0, NULL, TRUE,
1077 : : { tariff1, }, 2018, 2, 4, 1, 30, 0, NULL, FALSE },
1078 : : /* Transition from an unmetered period and a metered period in one
1079 : : * tariff, to a metered period in one and an unmetered period in another.
1080 : : * Since the scheduler requires *all* connections to be safe in order to
1081 : : * start a download, this leads to very limited active downloads. */
1082 : : { { tariff1, tariff2, }, 2018, 2, 4, 0, 30, 0, NULL, FALSE,
1083 : : { tariff1, tariff2, }, 2018, 2, 4, 1, 15, 0, NULL, FALSE },
1084 : : { { tariff1, tariff2, }, 2018, 2, 4, 1, 45, 0, NULL, FALSE,
1085 : : { tariff1, tariff2, }, 2018, 2, 4, 1, 59, 0, NULL, FALSE },
1086 : : { { tariff1, tariff2, }, 2018, 2, 4, 2, 0, 0, NULL, TRUE,
1087 : : { tariff1, tariff2, }, 2018, 2, 4, 2, 15, 0, NULL, TRUE },
1088 : : /* Timezone change in metered and unmetered periods. The underlying UTC
1089 : : * time may change at the same time, to either give a timezone change only
1090 : : * (where the underlying UTC time is the same) or a timezone and time
1091 : : * change such that the resolved wall clock time stays the same */
1092 : : /* Change timezone only: */
1093 : : { { tariff1, }, 2018, 2, 3, 15, 30, 0, "Europe/London" /* UTC+0 */, TRUE,
1094 : : { tariff1, }, 2018, 2, 3, 15, 30, 0, "Australia/Brisbane" /* UTC+10 */, TRUE },
1095 : : /* Keep wall clock time unchanged: */
1096 : : { { tariff1, }, 2018, 2, 3, 1, 30, 0, "Australia/Brisbane" /* UTC+10 */, FALSE,
1097 : : { tariff1, }, 2018, 2, 3, 11, 30, 0, "Europe/London" /* UTC+0 */, TRUE },
1098 : :
1099 : : { { tariff1, }, 2018, 2, 3, 16, 30, 0, "Europe/London" /* UTC+0 */, TRUE,
1100 : : { tariff1, }, 2018, 2, 3, 16, 30, 0, "Europe/Berlin" /* UTC+1 */, TRUE },
1101 : : { { tariff1, }, 2018, 2, 3, 15, 30, 0, "Europe/Berlin" /* UTC+1 */, TRUE,
1102 : : { tariff1, }, 2018, 2, 3, 16, 30, 0, "Europe/London" /* UTC+0 */, TRUE },
1103 : :
1104 : : { { tariff1, }, 2018, 2, 3, 1, 30, 0, "Europe/London" /* UTC+0 */, FALSE,
1105 : : { tariff1, }, 2018, 2, 3, 1, 30, 0, "Australia/Brisbane" /* UTC+10 */, FALSE },
1106 : : { { tariff1, }, 2018, 2, 2, 15, 30, 0, "Australia/Brisbane" /* UTC+10 */, TRUE,
1107 : : { tariff1, }, 2018, 2, 3, 1, 30, 0, "Europe/London" /* UTC+0 */, FALSE },
1108 : :
1109 : : { { tariff1, }, 2018, 2, 3, 1, 30, 0, "Europe/London", FALSE,
1110 : : { tariff1, }, 2018, 2, 3, 1, 30, 0, "Europe/Isle_of_Man", FALSE },
1111 : : /* Test different times in (and out of) a tariff which doesn’t repeat. */
1112 : : { { tariff3, }, 2017, 12, 30, 0, 0, 0, NULL, TRUE,
1113 : : { tariff3, }, 2018, 1, 1, 1, 30, 0, NULL, FALSE },
1114 : : { { tariff3, }, 2018, 1, 1, 0, 0, 0, NULL, FALSE,
1115 : : { tariff3, }, 2018, 1, 2, 0, 0, 0, NULL, TRUE },
1116 : : };
1117 : :
1118 [ + + ]: 18 : for (gsize i = 0; i < G_N_ELEMENTS (transitions); i++)
1119 : : {
1120 : : /* Set up the dates/times. The date/time figures in the test vector are
1121 : : * given in UTC and converted to the stated timezone, so they represent
1122 : : * the same instant as the UTC time, but with a likely different wall
1123 : : * clock time. */
1124 : 34 : g_autoptr(GTimeZone) state1_tz = time_zone_new (transitions[i].state1_tz);
1125 : 34 : g_autoptr(GDateTime) state1_time_utc = g_date_time_new_utc (transitions[i].state1_year,
1126 : : transitions[i].state1_month,
1127 : : transitions[i].state1_day,
1128 : : transitions[i].state1_hour,
1129 : : transitions[i].state1_minute,
1130 : : transitions[i].state1_seconds);
1131 : 34 : g_autoptr(GDateTime) state1_time = g_date_time_to_timezone (state1_time_utc,
1132 : : state1_tz);
1133 : :
1134 : 34 : g_autoptr(GTimeZone) state2_tz = time_zone_new (transitions[i].state2_tz);
1135 : 34 : g_autoptr(GDateTime) state2_time_utc = g_date_time_new_utc (transitions[i].state2_year,
1136 : : transitions[i].state2_month,
1137 : : transitions[i].state2_day,
1138 : : transitions[i].state2_hour,
1139 : : transitions[i].state2_minute,
1140 : : transitions[i].state2_seconds);
1141 : 34 : g_autoptr(GDateTime) state2_time = g_date_time_to_timezone (state2_time_utc,
1142 : : state2_tz);
1143 : :
1144 : : /* We only care about transitions within a given connection in this test,
1145 : : * so the set of connections available in states 1 and 2 must be the same */
1146 [ + + ]: 68 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_tariffs); j++)
1147 [ - + ]: 51 : g_assert ((transitions[i].state1_tariffs[j] == NULL) ==
1148 : : (transitions[i].state2_tariffs[j] == NULL));
1149 : : /* Similarly, we can vary either the wall clock time, or the connection
1150 : : * tariff, between states 1 and 2 — but varying both would give too many
1151 : : * axes of freedom in the test. Ensure only one varies. */
1152 : 17 : gboolean all_tariffs_equal = TRUE;
1153 [ + + ]: 68 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_tariffs); j++)
1154 : 51 : all_tariffs_equal =
1155 [ + + ]: 98 : all_tariffs_equal &&
1156 [ + + ]: 47 : transitions[i].state1_tariffs[j] == transitions[i].state2_tariffs[j];
1157 [ + + - + ]: 17 : g_assert (all_tariffs_equal ||
1158 : : g_date_time_equal (state1_time, state2_time));
1159 : : /* We want a monotonic wall clock. */
1160 [ - + ]: 17 : g_assert_cmpint (g_date_time_compare (state1_time, state2_time), <=, 0);
1161 : :
1162 : 17 : g_test_message ("Transition test %" G_GSIZE_FORMAT " of %" G_GSIZE_FORMAT,
1163 : : i + 1, G_N_ELEMENTS (transitions));
1164 : :
1165 : 17 : gboolean initial_allow_downloads = mws_scheduler_get_allow_downloads (fixture->scheduler);
1166 : :
1167 : : /* Set the time in state 1. */
1168 : 17 : mws_clock_dummy_set_time_zone (MWS_CLOCK_DUMMY (fixture->clock), state1_tz);
1169 : 17 : mws_clock_dummy_set_time (MWS_CLOCK_DUMMY (fixture->clock), state1_time);
1170 : :
1171 : : /* Set up the connections in state 1. */
1172 : 17 : g_autoptr(GHashTable) state1_connections =
1173 : 17 : g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1174 : :
1175 [ + + ]: 68 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_tariffs); j++)
1176 : : {
1177 [ + + ]: 51 : if (transitions[i].state1_tariffs[j] != NULL)
1178 : : {
1179 : 40 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
1180 : :
1181 : 40 : g_autofree MwsConnectionDetails *connection = g_new0 (MwsConnectionDetails, 1);
1182 : 20 : connection->metered = MWS_METERED_NO;
1183 : 20 : connection->allow_downloads_when_metered = FALSE;
1184 : 20 : connection->allow_downloads = TRUE;
1185 : 20 : connection->tariff = transitions[i].state1_tariffs[j];
1186 : :
1187 : 20 : g_hash_table_insert (state1_connections, g_steal_pointer (&id),
1188 : : g_steal_pointer (&connection));
1189 : : }
1190 : : }
1191 : :
1192 : 17 : mws_connection_monitor_dummy_update_connections (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
1193 : : state1_connections, NULL);
1194 : :
1195 : : /* Due to the test setup, we always expect allow-downloads=true. */
1196 [ - + ]: 17 : if (!initial_allow_downloads)
1197 [ # # # # : 0 : mws_signal_logger_assert_emission_pop (fixture->scheduler_signals,
# # ]
1198 : : fixture->scheduler,
1199 : : "notify::allow-downloads", NULL);
1200 [ - + ]: 17 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
1201 : :
1202 : : /* Add a single entry to the scheduler. */
1203 : 34 : g_autoptr(MwsScheduleEntry) entry = mws_schedule_entry_new (":owner.1");
1204 : :
1205 : 34 : g_autoptr(GPtrArray) entry_array = g_ptr_array_new_with_free_func (NULL);
1206 : 17 : g_ptr_array_add (entry_array, entry);
1207 : :
1208 : 17 : mws_scheduler_update_entries (fixture->scheduler, entry_array, NULL, &local_error);
1209 [ - + ]: 17 : g_assert_no_error (local_error);
1210 : 17 : assert_entries_changed_signals (fixture, entry_array, NULL,
1211 [ + + ]: 17 : (transitions[i].state1_expected_active ? entry_array : NULL),
1212 : : NULL, NULL);
1213 : :
1214 : : /* Change to the next time. */
1215 : 17 : mws_clock_dummy_set_time_zone (MWS_CLOCK_DUMMY (fixture->clock), state2_tz);
1216 : 17 : mws_clock_dummy_set_time (MWS_CLOCK_DUMMY (fixture->clock), state2_time);
1217 : :
1218 : : /* Change to the next connection state. */
1219 [ + + ]: 68 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state2_tariffs); j++)
1220 : : {
1221 [ + + ]: 51 : if (transitions[i].state2_tariffs[j] != NULL)
1222 : : {
1223 : 40 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
1224 : :
1225 : 40 : g_autofree MwsConnectionDetails *connection = g_new0 (MwsConnectionDetails, 1);
1226 : 20 : connection->metered = MWS_METERED_NO;
1227 : 20 : connection->allow_downloads_when_metered = FALSE;
1228 : 20 : connection->allow_downloads = TRUE;
1229 : 20 : connection->tariff = transitions[i].state2_tariffs[j];
1230 : :
1231 : 20 : mws_connection_monitor_dummy_update_connection (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
1232 : : id, connection);
1233 : : }
1234 : : }
1235 : :
1236 [ + + ]: 17 : if (transitions[i].state1_expected_active == transitions[i].state2_expected_active)
1237 [ - + ]: 11 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
1238 [ + + ]: 6 : else if (transitions[i].state1_expected_active)
1239 : 4 : assert_entries_changed_signals (fixture, NULL, NULL, NULL, entry_array, NULL);
1240 : : else
1241 : 2 : assert_entries_changed_signals (fixture, NULL, NULL, entry_array, NULL, NULL);
1242 : :
1243 : : /* Change back to the previous time. This might not be very realistic,
1244 : : * but the scheduler should deal with it. */
1245 : 17 : mws_clock_dummy_set_time_zone (MWS_CLOCK_DUMMY (fixture->clock), state1_tz);
1246 : 17 : mws_clock_dummy_set_time (MWS_CLOCK_DUMMY (fixture->clock), state1_time);
1247 : :
1248 : : /* Change back to the previous connection state. The entry’s scheduled
1249 : : * state should always be the same as before. */
1250 [ + + ]: 68 : for (gsize j = 0; j < G_N_ELEMENTS (transitions[i].state1_tariffs); j++)
1251 : : {
1252 [ + + ]: 51 : if (transitions[i].state1_tariffs[j] != NULL)
1253 : : {
1254 : 40 : g_autofree gchar *id = g_strdup_printf ("connection%" G_GSIZE_FORMAT, j);
1255 : :
1256 : 40 : g_autofree MwsConnectionDetails *connection = g_new0 (MwsConnectionDetails, 1);
1257 : 20 : connection->metered = MWS_METERED_NO;
1258 : 20 : connection->allow_downloads_when_metered = FALSE;
1259 : 20 : connection->allow_downloads = TRUE;
1260 : 20 : connection->tariff = transitions[i].state1_tariffs[j];
1261 : :
1262 : 20 : mws_connection_monitor_dummy_update_connection (MWS_CONNECTION_MONITOR_DUMMY (fixture->connection_monitor),
1263 : : id, connection);
1264 : : }
1265 : : }
1266 : :
1267 [ + + ]: 17 : if (transitions[i].state1_expected_active == transitions[i].state2_expected_active)
1268 [ - + ]: 11 : mws_signal_logger_assert_no_emissions (fixture->scheduler_signals);
1269 [ + + ]: 6 : else if (transitions[i].state2_expected_active)
1270 : 2 : assert_entries_changed_signals (fixture, NULL, NULL, NULL, entry_array, NULL);
1271 : : else
1272 : 4 : assert_entries_changed_signals (fixture, NULL, NULL, entry_array, NULL, NULL);
1273 : :
1274 : : /* Clean up. */
1275 : 17 : teardown (fixture, test_data);
1276 : 17 : setup (fixture, test_data);
1277 : : }
1278 : 1 : }
1279 : :
1280 : : int
1281 : 1 : main (int argc,
1282 : : char **argv)
1283 : : {
1284 : 1 : setlocale (LC_ALL, "");
1285 : 1 : g_test_init (&argc, &argv, NULL);
1286 : :
1287 : 1 : const TestData standard_data =
1288 : : {
1289 : : .max_active_entries = 1,
1290 : : };
1291 : 1 : const TestData max_active_entries_data =
1292 : : {
1293 : : .max_active_entries = 2,
1294 : : };
1295 : :
1296 : 1 : g_test_add_func ("/scheduler/construction", test_scheduler_construction);
1297 : 1 : g_test_add ("/scheduler/entries", Fixture, &standard_data, setup,
1298 : : test_scheduler_entries, teardown);
1299 : 1 : g_test_add ("/scheduler/entries/remove-for-owner", Fixture,
1300 : : &standard_data, setup,
1301 : : test_scheduler_entries_remove_for_owner, teardown);
1302 : 1 : g_test_add ("/scheduler/properties", Fixture, &standard_data, setup,
1303 : : test_scheduler_properties, teardown);
1304 : 1 : g_test_add ("/scheduler/scheduling/entry-priorities", Fixture,
1305 : : &standard_data, setup,
1306 : : test_scheduler_scheduling_entry_priorities, teardown);
1307 : 1 : g_test_add ("/scheduler/scheduling/peer-priorities", Fixture,
1308 : : &standard_data, setup,
1309 : : test_scheduler_scheduling_peer_priorities, teardown);
1310 : 1 : g_test_add ("/scheduler/scheduling/max-active-entries", Fixture,
1311 : : &max_active_entries_data, setup,
1312 : : test_scheduler_scheduling_max_active_entries, teardown);
1313 : 1 : g_test_add ("/scheduler/scheduling/peer-vanished", Fixture,
1314 : : &standard_data, setup,
1315 : : test_scheduler_scheduling_peer_vanished, teardown);
1316 : 1 : g_test_add ("/scheduler/scheduling/metered-connection", Fixture,
1317 : : &standard_data, setup,
1318 : : test_scheduler_scheduling_metered_connection, teardown);
1319 : 1 : g_test_add ("/scheduler/scheduling/tariff", Fixture,
1320 : : &standard_data, setup,
1321 : : test_scheduler_scheduling_tariff, teardown);
1322 : :
1323 : 1 : return g_test_run ();
1324 : : }
|