]> Git Repo - qemu.git/blob - tests/test-throttle.c
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
[qemu.git] / tests / test-throttle.c
1 /*
2  * Throttle infrastructure tests
3  *
4  * Copyright Nodalink, EURL. 2013-2014
5  * Copyright Igalia, S.L. 2015
6  *
7  * Authors:
8  *  BenoĆ®t Canet     <[email protected]>
9  *  Alberto Garcia   <[email protected]>
10  *
11  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
12  * See the COPYING.LIB file in the top-level directory.
13  */
14
15 #include "qemu/osdep.h"
16 #include <glib.h>
17 #include <math.h>
18 #include "block/aio.h"
19 #include "qapi/error.h"
20 #include "qemu/throttle.h"
21 #include "qemu/error-report.h"
22 #include "block/throttle-groups.h"
23 #include "sysemu/block-backend.h"
24
25 static AioContext     *ctx;
26 static LeakyBucket    bkt;
27 static ThrottleConfig cfg;
28 static ThrottleState  ts;
29 static ThrottleTimers tt;
30
31 /* useful function */
32 static bool double_cmp(double x, double y)
33 {
34     return fabsl(x - y) < 1e-6;
35 }
36
37 /* tests for single bucket operations */
38 static void test_leak_bucket(void)
39 {
40     throttle_config_init(&cfg);
41     bkt = cfg.buckets[THROTTLE_BPS_TOTAL];
42
43     /* set initial value */
44     bkt.avg = 150;
45     bkt.max = 15;
46     bkt.level = 1.5;
47
48     /* leak an op work of time */
49     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
50     g_assert(bkt.avg == 150);
51     g_assert(bkt.max == 15);
52     g_assert(double_cmp(bkt.level, 0.5));
53
54     /* leak again emptying the bucket */
55     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
56     g_assert(bkt.avg == 150);
57     g_assert(bkt.max == 15);
58     g_assert(double_cmp(bkt.level, 0));
59
60     /* check that the bucket level won't go lower */
61     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
62     g_assert(bkt.avg == 150);
63     g_assert(bkt.max == 15);
64     g_assert(double_cmp(bkt.level, 0));
65
66     /* check that burst_level leaks correctly */
67     bkt.burst_level = 6;
68     bkt.max = 250;
69     bkt.burst_length = 2; /* otherwise burst_level will not leak */
70     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
71     g_assert(double_cmp(bkt.burst_level, 3.5));
72
73     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
74     g_assert(double_cmp(bkt.burst_level, 1));
75
76     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
77     g_assert(double_cmp(bkt.burst_level, 0));
78
79     throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 100);
80     g_assert(double_cmp(bkt.burst_level, 0));
81 }
82
83 static void test_compute_wait(void)
84 {
85     unsigned i;
86     int64_t wait;
87     int64_t result;
88
89     throttle_config_init(&cfg);
90     bkt = cfg.buckets[THROTTLE_BPS_TOTAL];
91
92     /* no operation limit set */
93     bkt.avg = 0;
94     bkt.max = 15;
95     bkt.level = 1.5;
96     wait = throttle_compute_wait(&bkt);
97     g_assert(!wait);
98
99     /* zero delta */
100     bkt.avg = 150;
101     bkt.max = 15;
102     bkt.level = 15;
103     wait = throttle_compute_wait(&bkt);
104     g_assert(!wait);
105
106     /* below zero delta */
107     bkt.avg = 150;
108     bkt.max = 15;
109     bkt.level = 9;
110     wait = throttle_compute_wait(&bkt);
111     g_assert(!wait);
112
113     /* half an operation above max */
114     bkt.avg = 150;
115     bkt.max = 15;
116     bkt.level = 15.5;
117     wait = throttle_compute_wait(&bkt);
118     /* time required to do half an operation */
119     result = (int64_t)  NANOSECONDS_PER_SECOND / 150 / 2;
120     g_assert(wait == result);
121
122     /* Perform I/O for 2.2 seconds at a rate of bkt.max */
123     bkt.burst_length = 2;
124     bkt.level = 0;
125     bkt.avg = 10;
126     bkt.max = 200;
127     for (i = 0; i < 22; i++) {
128         double units = bkt.max / 10;
129         bkt.level += units;
130         bkt.burst_level += units;
131         throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 10);
132         wait = throttle_compute_wait(&bkt);
133         g_assert(double_cmp(bkt.burst_level, 0));
134         g_assert(double_cmp(bkt.level, (i + 1) * (bkt.max - bkt.avg) / 10));
135         /* We can do bursts for the 2 seconds we have configured in
136          * burst_length. We have 100 extra miliseconds of burst
137          * because bkt.level has been leaking during this time.
138          * After that, we have to wait. */
139         result = i < 21 ? 0 : 1.8 * NANOSECONDS_PER_SECOND;
140         g_assert(wait == result);
141     }
142 }
143
144 /* functions to test ThrottleState initialization/destroy methods */
145 static void read_timer_cb(void *opaque)
146 {
147 }
148
149 static void write_timer_cb(void *opaque)
150 {
151 }
152
153 static void test_init(void)
154 {
155     int i;
156
157     /* fill the structures with crap */
158     memset(&ts, 1, sizeof(ts));
159     memset(&tt, 1, sizeof(tt));
160
161     /* init structures */
162     throttle_init(&ts);
163     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
164                          read_timer_cb, write_timer_cb, &ts);
165
166     /* check initialized fields */
167     g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL);
168     g_assert(tt.timers[0]);
169     g_assert(tt.timers[1]);
170
171     /* check other fields where cleared */
172     g_assert(!ts.previous_leak);
173     g_assert(!ts.cfg.op_size);
174     for (i = 0; i < BUCKETS_COUNT; i++) {
175         g_assert(!ts.cfg.buckets[i].avg);
176         g_assert(!ts.cfg.buckets[i].max);
177         g_assert(!ts.cfg.buckets[i].level);
178     }
179
180     throttle_timers_destroy(&tt);
181 }
182
183 static void test_destroy(void)
184 {
185     int i;
186     throttle_init(&ts);
187     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
188                          read_timer_cb, write_timer_cb, &ts);
189     throttle_timers_destroy(&tt);
190     for (i = 0; i < 2; i++) {
191         g_assert(!tt.timers[i]);
192     }
193 }
194
195 /* function to test throttle_config and throttle_get_config */
196 static void test_config_functions(void)
197 {
198     int i;
199     ThrottleConfig orig_cfg, final_cfg;
200
201     orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153;
202     orig_cfg.buckets[THROTTLE_BPS_READ].avg  = 56;
203     orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1;
204
205     orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150;
206     orig_cfg.buckets[THROTTLE_OPS_READ].avg  = 69;
207     orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23;
208
209     orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; /* should be corrected */
210     orig_cfg.buckets[THROTTLE_BPS_READ].max  = 1; /* should not be corrected */
211     orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120;
212
213     orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150;
214     orig_cfg.buckets[THROTTLE_OPS_READ].max  = 400;
215     orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500;
216
217     orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45;
218     orig_cfg.buckets[THROTTLE_BPS_READ].level  = 65;
219     orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23;
220
221     orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1;
222     orig_cfg.buckets[THROTTLE_OPS_READ].level  = 90;
223     orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75;
224
225     orig_cfg.op_size = 1;
226
227     throttle_init(&ts);
228     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
229                          read_timer_cb, write_timer_cb, &ts);
230     /* structure reset by throttle_init previous_leak should be null */
231     g_assert(!ts.previous_leak);
232     throttle_config(&ts, &tt, &orig_cfg);
233
234     /* has previous leak been initialized by throttle_config ? */
235     g_assert(ts.previous_leak);
236
237     /* get back the fixed configuration */
238     throttle_get_config(&ts, &final_cfg);
239
240     throttle_timers_destroy(&tt);
241
242     g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
243     g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg  == 56);
244     g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1);
245
246     g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150);
247     g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg  == 69);
248     g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23);
249
250     g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 15.3);/* fixed */
251     g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max  == 1);   /* not fixed */
252     g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120);
253
254     g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150);
255     g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max  == 400);
256     g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500);
257
258     g_assert(final_cfg.op_size == 1);
259
260     /* check bucket have been cleared */
261     for (i = 0; i < BUCKETS_COUNT; i++) {
262         g_assert(!final_cfg.buckets[i].level);
263     }
264 }
265
266 /* functions to test is throttle is enabled by a config */
267 static void set_cfg_value(bool is_max, int index, int value)
268 {
269     if (is_max) {
270         cfg.buckets[index].max = value;
271         /* If max is set, avg should never be 0 */
272         cfg.buckets[index].avg = MAX(cfg.buckets[index].avg, 1);
273     } else {
274         cfg.buckets[index].avg = value;
275     }
276 }
277
278 static void test_enabled(void)
279 {
280     int i;
281
282     throttle_config_init(&cfg);
283     g_assert(!throttle_enabled(&cfg));
284
285     for (i = 0; i < BUCKETS_COUNT; i++) {
286         throttle_config_init(&cfg);
287         set_cfg_value(false, i, 150);
288         g_assert(throttle_enabled(&cfg));
289     }
290
291     for (i = 0; i < BUCKETS_COUNT; i++) {
292         throttle_config_init(&cfg);
293         set_cfg_value(false, i, -150);
294         g_assert(!throttle_enabled(&cfg));
295     }
296 }
297
298 /* tests functions for throttle_conflicting */
299
300 static void test_conflicts_for_one_set(bool is_max,
301                                        int total,
302                                        int read,
303                                        int write)
304 {
305     throttle_config_init(&cfg);
306     g_assert(throttle_is_valid(&cfg, NULL));
307
308     set_cfg_value(is_max, total, 1);
309     set_cfg_value(is_max, read,  1);
310     g_assert(!throttle_is_valid(&cfg, NULL));
311
312     throttle_config_init(&cfg);
313     set_cfg_value(is_max, total, 1);
314     set_cfg_value(is_max, write, 1);
315     g_assert(!throttle_is_valid(&cfg, NULL));
316
317     throttle_config_init(&cfg);
318     set_cfg_value(is_max, total, 1);
319     set_cfg_value(is_max, read,  1);
320     set_cfg_value(is_max, write, 1);
321     g_assert(!throttle_is_valid(&cfg, NULL));
322
323     throttle_config_init(&cfg);
324     set_cfg_value(is_max, total, 1);
325     g_assert(throttle_is_valid(&cfg, NULL));
326
327     throttle_config_init(&cfg);
328     set_cfg_value(is_max, read,  1);
329     set_cfg_value(is_max, write, 1);
330     g_assert(throttle_is_valid(&cfg, NULL));
331 }
332
333 static void test_conflicting_config(void)
334 {
335     /* bps average conflicts */
336     test_conflicts_for_one_set(false,
337                                THROTTLE_BPS_TOTAL,
338                                THROTTLE_BPS_READ,
339                                THROTTLE_BPS_WRITE);
340
341     /* ops average conflicts */
342     test_conflicts_for_one_set(false,
343                                THROTTLE_OPS_TOTAL,
344                                THROTTLE_OPS_READ,
345                                THROTTLE_OPS_WRITE);
346
347     /* bps average conflicts */
348     test_conflicts_for_one_set(true,
349                                THROTTLE_BPS_TOTAL,
350                                THROTTLE_BPS_READ,
351                                THROTTLE_BPS_WRITE);
352     /* ops average conflicts */
353     test_conflicts_for_one_set(true,
354                                THROTTLE_OPS_TOTAL,
355                                THROTTLE_OPS_READ,
356                                THROTTLE_OPS_WRITE);
357 }
358 /* functions to test the throttle_is_valid function */
359 static void test_is_valid_for_value(int value, bool should_be_valid)
360 {
361     int is_max, index;
362     for (is_max = 0; is_max < 2; is_max++) {
363         for (index = 0; index < BUCKETS_COUNT; index++) {
364             throttle_config_init(&cfg);
365             set_cfg_value(is_max, index, value);
366             g_assert(throttle_is_valid(&cfg, NULL) == should_be_valid);
367         }
368     }
369 }
370
371 static void test_is_valid(void)
372 {
373     /* negative number are invalid */
374     test_is_valid_for_value(-1, false);
375     /* zero are valids */
376     test_is_valid_for_value(0, true);
377     /* positives numers are valids */
378     test_is_valid_for_value(1, true);
379 }
380
381 static void test_max_is_missing_limit(void)
382 {
383     int i;
384
385     for (i = 0; i < BUCKETS_COUNT; i++) {
386         throttle_config_init(&cfg);
387         cfg.buckets[i].max = 100;
388         cfg.buckets[i].avg = 0;
389         g_assert(!throttle_is_valid(&cfg, NULL));
390
391         cfg.buckets[i].max = 0;
392         cfg.buckets[i].avg = 0;
393         g_assert(throttle_is_valid(&cfg, NULL));
394
395         cfg.buckets[i].max = 0;
396         cfg.buckets[i].avg = 100;
397         g_assert(throttle_is_valid(&cfg, NULL));
398     }
399 }
400
401 static void test_have_timer(void)
402 {
403     /* zero structures */
404     memset(&ts, 0, sizeof(ts));
405     memset(&tt, 0, sizeof(tt));
406
407     /* no timer set should return false */
408     g_assert(!throttle_timers_are_initialized(&tt));
409
410     /* init structures */
411     throttle_init(&ts);
412     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
413                          read_timer_cb, write_timer_cb, &ts);
414
415     /* timer set by init should return true */
416     g_assert(throttle_timers_are_initialized(&tt));
417
418     throttle_timers_destroy(&tt);
419 }
420
421 static void test_detach_attach(void)
422 {
423     /* zero structures */
424     memset(&ts, 0, sizeof(ts));
425     memset(&tt, 0, sizeof(tt));
426
427     /* init the structure */
428     throttle_init(&ts);
429     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
430                          read_timer_cb, write_timer_cb, &ts);
431
432     /* timer set by init should return true */
433     g_assert(throttle_timers_are_initialized(&tt));
434
435     /* timer should no longer exist after detaching */
436     throttle_timers_detach_aio_context(&tt);
437     g_assert(!throttle_timers_are_initialized(&tt));
438
439     /* timer should exist again after attaching */
440     throttle_timers_attach_aio_context(&tt, ctx);
441     g_assert(throttle_timers_are_initialized(&tt));
442
443     throttle_timers_destroy(&tt);
444 }
445
446 static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
447                 int size,                   /* size of the operation to do */
448                 double avg,                 /* io limit */
449                 uint64_t op_size,           /* ideal size of an io */
450                 double total_result,
451                 double read_result,
452                 double write_result)
453 {
454     BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
455                                    THROTTLE_BPS_READ,
456                                    THROTTLE_BPS_WRITE, },
457                                  { THROTTLE_OPS_TOTAL,
458                                    THROTTLE_OPS_READ,
459                                    THROTTLE_OPS_WRITE, } };
460     ThrottleConfig cfg;
461     BucketType index;
462     int i;
463
464     for (i = 0; i < 3; i++) {
465         BucketType index = to_test[is_ops][i];
466         cfg.buckets[index].avg = avg;
467     }
468
469     cfg.op_size = op_size;
470
471     throttle_init(&ts);
472     throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL,
473                          read_timer_cb, write_timer_cb, &ts);
474     throttle_config(&ts, &tt, &cfg);
475
476     /* account a read */
477     throttle_account(&ts, false, size);
478     /* account a write */
479     throttle_account(&ts, true, size);
480
481     /* check total result */
482     index = to_test[is_ops][0];
483     if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
484         return false;
485     }
486
487     /* check read result */
488     index = to_test[is_ops][1];
489     if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
490         return false;
491     }
492
493     /* check write result */
494     index = to_test[is_ops][2];
495     if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
496         return false;
497     }
498
499     throttle_timers_destroy(&tt);
500
501     return true;
502 }
503
504 static void test_accounting(void)
505 {
506     /* tests for bps */
507
508     /* op of size 1 */
509     g_assert(do_test_accounting(false,
510                                 1 * 512,
511                                 150,
512                                 0,
513                                 1024,
514                                 512,
515                                 512));
516
517     /* op of size 2 */
518     g_assert(do_test_accounting(false,
519                                 2 * 512,
520                                 150,
521                                 0,
522                                 2048,
523                                 1024,
524                                 1024));
525
526     /* op of size 2 and orthogonal parameter change */
527     g_assert(do_test_accounting(false,
528                                 2 * 512,
529                                 150,
530                                 17,
531                                 2048,
532                                 1024,
533                                 1024));
534
535
536     /* tests for ops */
537
538     /* op of size 1 */
539     g_assert(do_test_accounting(true,
540                                 1 * 512,
541                                 150,
542                                 0,
543                                 2,
544                                 1,
545                                 1));
546
547     /* op of size 2 */
548     g_assert(do_test_accounting(true,
549                                 2 *  512,
550                                 150,
551                                 0,
552                                 2,
553                                 1,
554                                 1));
555
556     /* jumbo op accounting fragmentation : size 64 with op size of 13 units */
557     g_assert(do_test_accounting(true,
558                                 64 * 512,
559                                 150,
560                                 13 * 512,
561                                 (64.0 * 2) / 13,
562                                 (64.0 / 13),
563                                 (64.0 / 13)));
564
565     /* same with orthogonal parameters changes */
566     g_assert(do_test_accounting(true,
567                                 64 * 512,
568                                 300,
569                                 13 * 512,
570                                 (64.0 * 2) / 13,
571                                 (64.0 / 13),
572                                 (64.0 / 13)));
573 }
574
575 static void test_groups(void)
576 {
577     ThrottleConfig cfg1, cfg2;
578     BlockBackend *blk1, *blk2, *blk3;
579     BlockBackendPublic *blkp1, *blkp2, *blkp3;
580
581     blk1 = blk_new_with_bs(&error_abort);
582     blk2 = blk_new_with_bs(&error_abort);
583     blk3 = blk_new_with_bs(&error_abort);
584
585     blkp1 = blk_get_public(blk1);
586     blkp2 = blk_get_public(blk2);
587     blkp3 = blk_get_public(blk3);
588
589     g_assert(blkp1->throttle_state == NULL);
590     g_assert(blkp2->throttle_state == NULL);
591     g_assert(blkp3->throttle_state == NULL);
592
593     throttle_group_register_blk(blk1, "bar");
594     throttle_group_register_blk(blk2, "foo");
595     throttle_group_register_blk(blk3, "bar");
596
597     g_assert(blkp1->throttle_state != NULL);
598     g_assert(blkp2->throttle_state != NULL);
599     g_assert(blkp3->throttle_state != NULL);
600
601     g_assert(!strcmp(throttle_group_get_name(blk1), "bar"));
602     g_assert(!strcmp(throttle_group_get_name(blk2), "foo"));
603     g_assert(blkp1->throttle_state == blkp3->throttle_state);
604
605     /* Setting the config of a group member affects the whole group */
606     throttle_config_init(&cfg1);
607     cfg1.buckets[THROTTLE_BPS_READ].avg  = 500000;
608     cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000;
609     cfg1.buckets[THROTTLE_OPS_READ].avg  = 20000;
610     cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000;
611     throttle_group_config(blk1, &cfg1);
612
613     throttle_group_get_config(blk1, &cfg1);
614     throttle_group_get_config(blk3, &cfg2);
615     g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
616
617     cfg2.buckets[THROTTLE_BPS_READ].avg  = 4547;
618     cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349;
619     cfg2.buckets[THROTTLE_OPS_READ].avg  = 123;
620     cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86;
621     throttle_group_config(blk3, &cfg1);
622
623     throttle_group_get_config(blk1, &cfg1);
624     throttle_group_get_config(blk3, &cfg2);
625     g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1)));
626
627     throttle_group_unregister_blk(blk1);
628     throttle_group_unregister_blk(blk2);
629     throttle_group_unregister_blk(blk3);
630
631     g_assert(blkp1->throttle_state == NULL);
632     g_assert(blkp2->throttle_state == NULL);
633     g_assert(blkp3->throttle_state == NULL);
634 }
635
636 int main(int argc, char **argv)
637 {
638     qemu_init_main_loop(&error_fatal);
639     ctx = qemu_get_aio_context();
640     bdrv_init();
641
642     do {} while (g_main_context_iteration(NULL, false));
643
644     /* tests in the same order as the header function declarations */
645     g_test_init(&argc, &argv, NULL);
646     g_test_add_func("/throttle/leak_bucket",        test_leak_bucket);
647     g_test_add_func("/throttle/compute_wait",       test_compute_wait);
648     g_test_add_func("/throttle/init",               test_init);
649     g_test_add_func("/throttle/destroy",            test_destroy);
650     g_test_add_func("/throttle/have_timer",         test_have_timer);
651     g_test_add_func("/throttle/detach_attach",      test_detach_attach);
652     g_test_add_func("/throttle/config/enabled",     test_enabled);
653     g_test_add_func("/throttle/config/conflicting", test_conflicting_config);
654     g_test_add_func("/throttle/config/is_valid",    test_is_valid);
655     g_test_add_func("/throttle/config/max",         test_max_is_missing_limit);
656     g_test_add_func("/throttle/config_functions",   test_config_functions);
657     g_test_add_func("/throttle/accounting",         test_accounting);
658     g_test_add_func("/throttle/groups",             test_groups);
659     return g_test_run();
660 }
661
This page took 0.060736 seconds and 4 git commands to generate.