about summary refs log tree commit diff stats
path: root/build
Commit message (Collapse)AuthorAgeFilesLines
* 5865Kartik Agaram2020-01-021-10/+10
| | | | Give the bootstrap C++ program a less salient name.
* 5801 - move `tangle` to `tools/` dirKartik Agaram2019-12-071-14/+8
|
* 5797 - move `enumerate/` to `tools/` directoryKartik Agaram2019-12-071-4/+4
|
* 5796 - move treeshake to a new tools/ directoryKartik Agaram2019-12-071-5/+0
|
* 5793Kartik Agaram2019-12-051-0/+5
| | | | | | | | | Start of a new script called treeshake to emit stats for minimal line counts and binary sizes for all apps. It doesn't actually do any dead-code deletion yet. But it does build and run all apps successfully. (Except apps/mu; we'll ignore that for now. It's probably not being disciplined about identifying internal labels.)
* 5675 - move helpers from subx-common into layersKartik Agaram2019-09-191-50/+0
| | | | | | | | | | | | | | | | This undoes 5672 in favor of a new plan: Layers 000 - 099 are for running without syntax sugar. We use them for building syntax-sugar passes. Layers 100 and up are for running with all syntax sugar. The layers are arranged in approximate order so more phases rely on earlier layers than later ones. I plan to not use intermediate syntax sugar (just sigils without calls, or sigils and calls without braces) anywhere except in the specific passes implementing them.
* 5672 - move hex out of appsKartik Agaram2019-09-191-0/+5
|
* 5650 - support a second OS: sosoKartik Agaram2019-09-141-8/+10
| | | | | | | | | https://github.com/ozkl/soso + Much smaller than Linux; builds instantly + Supports graphics - No network support - Doesn't work on a cloud server (yet?)
* 5649Kartik Agaram2019-09-141-4/+4
|
* 5647 - experimental support for swapping OSKartik Agaram2019-09-111-4/+4
|
* 5594 - rename 'desugar' to 'sigils'Kartik Agaram2019-08-311-1/+1
| | | | There's going to be multiple forms of syntax sugar going forward.
* Merge branch 'master' into desugarKartik Agaram2019-08-251-0/+3
|\
| * 5512 - don't rebuild apps by defaultKartik Agaram2019-08-141-1/+4
| | | | | | | | | | We basically only want to rebuild phases of the self-hosted translator when we run the self-hosted translator.
* | skip building apps when running a single testKartik Agaram2019-08-251-1/+1
| | | | | | | | Environment variables allow me to have non-local effects inside scripts.
* | .Kartik Agaram2019-08-131-0/+8
|/
* 5485 - promote SubX to top-levelKartik Agaram2019-07-271-0/+141
|
* 4216 - include simpler alternative to build scriptKartik K. Agaram2018-03-121-149/+0
|
* 4204Kartik K. Agaram2018-02-151-2/+2
|
* 3967Kartik K. Agaram2017-07-101-2/+3
|
* 3846Kartik K. Agaram2017-05-061-1/+1
| | | | Be more robust to stray files with numeric prefixes.
* 3835Kartik K. Agaram2017-04-181-9/+3
|
* 3834Kartik K. Agaram2017-04-181-0/+2
|
* 3815Kartik K. Agaram2017-04-061-1/+1
| | | | | Turns out enabling profiling requires '-pg' to also be passed in to the linker. Might as well pass all flags everywhere.
* 3807Kartik K. Agaram2017-03-311-3/+3
| | | | Handle CFLAGS like "-g -O3 -pg" while compiling.
* 3712Kartik K. Agaram2016-12-261-15/+15
| | | | Let's start highlighting pipe stages better in shell scripts.
* 3680Kartik K. Agaram2016-11-171-16/+13
| | | | | Was there some reason I used a subshell? No reason recorded at bottom.
* 3551Kartik K. Agaram2016-10-221-1/+4
|
* 3548Kartik K. Agaram2016-10-221-1/+4
|
* 3546Kartik K. Agaram2016-10-221-0/+4
|
* 3540Kartik K. Agaram2016-10-211-2/+2
|
* 3538Kartik K. Agaram2016-10-201-1/+6
|
* 3534Kartik K. Agaram2016-10-201-33/+22
| | | | | | | | | | | | | | | | | | | | Streamline the build process. It's safest to always: a) check if each output is `older_than` the inputs necessary to construct it, and b) update the output only if something changed. However: i) We don't yet have helpers to do b) in all situations, and ii) combining a) and b) can cause `older_than` to spuriously report files being updated. So we'll always run exactly one of a) and b) and err on the side of keeping the output reliable, at the risk of occasionally updating a file unnecessarily and triggering unnecessary work downstream. Cross that bridge when we run into it.
* 3533Kartik K. Agaram2016-10-201-13/+35
| | | | Don't update autogenerated *_list files unless necessary.
* 3512Kartik K. Agaram2016-10-171-1/+1
|
* 3509Kartik K. Agaram2016-10-161-2/+3
|
* 3493Kartik K. Agaram2016-10-101-1/+1
|
* 3488 -Kartik K. Agaram2016-10-081-1/+1
| | | | | | I'd messed up termbox in commit 3443; it was weird how it failed though. The terminal got really sluggish to switch between windows when the edit/ app was running. And it stopped clearing the screen properly.
* 3487Kartik K. Agaram2016-10-081-1/+1
|
* 3475Kartik K. Agaram2016-10-071-22/+20
|
* 3474Kartik K. Agaram2016-10-071-20/+27
| | | | Don't print anything during build if there's nothing being built.
* 3459Kartik K. Agaram2016-10-071-2/+2
|
* 3452Kartik K. Agaram2016-10-061-21/+23
| | | | | | | | Fix the sense of a shell function. Somehow in all these years I hadn't realized that 0 is true and non-zero is false for purposes of *nix shells' `&&` and `||` operators. Suddenly Urbit doesn't seem so far out..
* 3450Kartik K. Agaram2016-10-061-2/+6
| | | | Purge remaining `makefile`s, without breaking CI.
* 3447 - drop dependence on GNU makeKartik K. Agaram2016-10-061-0/+117
A generic build system is overkill for such a small project, and it was adding complexity on OpenBSD which doesn't come with GNU make by default. In the process we also eliminate our reliance on bash and perl, at least for the core build script.
n270'>270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727







                      































                                                                                  
                           






                                                             










                                                                    














                                                                    

























                                                                      










                                                                
























                                                                          





































                                                                          





































                                                                          






































                                                                          







































                                                                             












































                                                                                 


                                           






























                                                                          







































                                                                                  





































                                                                                  


                                                      



























                                                                          






































                                                                          
                                                
 


























                                                                          
                                                 
 














                                                                          
                        




                                                   






                                                                 





                                              
                                     


                       


                                                                 



























                                                                          








































                                                                          




























                                                                          






































                                                                          
#include <stdarg.h>
#include <string.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdlib.h>

#include "xmpp/form.h"

xmpp_ctx_t* connection_get_ctx(void)
{
    return NULL;
}

static DataForm*
_new_form(void)
{
    DataForm *form = malloc(sizeof(DataForm));
    form->type = NULL;
    form->title = NULL;
    form->instructions = NULL;
    form->fields = NULL;
    form->var_to_tag = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
    form->tag_to_var = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
    form->tag_ac = NULL;

    return form;
}

static FormField*
_new_field(void)
{
    FormField *field = malloc(sizeof(FormField));
    field->label = NULL;
    field->type = NULL;
    field->description = NULL;
    field->required = FALSE;
    field->options = NULL;
    field->var = NULL;
    field->values = NULL;
    field->value_ac = NULL;

    return field;

}

void get_form_type_field_returns_null_no_fields(void **state)
{
    DataForm *form = _new_form();

    char *result = form_get_form_type_field(form);

    assert_null(result);

    form_destroy(form);
}

void get_form_type_field_returns_null_when_not_present(void **state)
{
    DataForm *form = _new_form();
    FormField *field = _new_field();
    field->var = strdup("var1");
    field->values = g_slist_append(field->values, strdup("value1"));
    form->fields = g_slist_append(form->fields, field);

    char *result = form_get_form_type_field(form);

    assert_null(result);

    form_destroy(form);
}

void get_form_type_field_returns_value_when_present(void **state)
{
    DataForm *form = _new_form();

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->values = g_slist_append(field1->values, strdup("value1"));
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("FORM_TYPE");
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    FormField *field3 = _new_field();
    field3->var = strdup("var3");
    field3->values = g_slist_append(field3->values, strdup("value3"));
    form->fields = g_slist_append(form->fields, field3);

    char *result = form_get_form_type_field(form);

    assert_string_equal(result, "value2");

    form_destroy(form);
}

void get_field_type_returns_unknown_when_no_fields(void **state)
{
    DataForm *form = _new_form();

    form_field_type_t result = form_get_field_type(form, "tag");

    assert_int_equal(result, FIELD_UNKNOWN);

    form_destroy(form);
}

void get_field_type_returns_correct_type(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_TEXT_SINGLE;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_TEXT_MULTI;
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    form_field_type_t result = form_get_field_type(form, "tag2");

    assert_int_equal(result, FIELD_TEXT_MULTI);

    form_destroy(form);
}

void set_value_adds_when_none(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_TEXT_SINGLE;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_LIST_SINGLE;
    form->fields = g_slist_append(form->fields, field2);

    form_set_value(form, "tag2", "a new value");

    int length = 0;
    char *value = NULL;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var2") == 0) {
            length = g_slist_length(field->values);
            value = field->values->data;
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_int_equal(length, 1);
    assert_string_equal(value, "a new value");

    form_destroy(form);
}

void set_value_updates_when_one(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_TEXT_SINGLE;
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_LIST_SINGLE;
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    form_set_value(form, "tag2", "a new value");

    int length = 0;
    char *value = NULL;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var2") == 0) {
            length = g_slist_length(field->values);
            value = field->values->data;
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_int_equal(length, 1);
    assert_string_equal(value, "a new value");

    form_destroy(form);
}

void add_unique_value_adds_when_none(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_JID_MULTI;
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_LIST_SINGLE;
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    gboolean ret = form_add_unique_value(form, "tag1", "me@server.com");

    int length = 0;
    char *value = NULL;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            value = field->values->data;
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(ret);
    assert_int_equal(length, 1);
    assert_string_equal(value, "me@server.com");

    form_destroy(form);
}

void add_unique_value_does_nothing_when_exists(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_JID_MULTI;
    field1->values = g_slist_append(field1->values, strdup("me@server.com"));
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_LIST_SINGLE;
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    gboolean ret = form_add_unique_value(form, "tag1", "me@server.com");

    int length = 0;
    char *value = NULL;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            value = field->values->data;
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_false(ret);
    assert_int_equal(length, 1);
    assert_string_equal(value, "me@server.com");

    form_destroy(form);
}

void add_unique_value_adds_when_doesnt_exist(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));
    g_hash_table_insert(form->tag_to_var, strdup("tag2"), strdup("var2"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_JID_MULTI;
    field1->values = g_slist_append(field1->values, strdup("dolan@server.com"));
    field1->values = g_slist_append(field1->values, strdup("kieran@server.com"));
    field1->values = g_slist_append(field1->values, strdup("chi@server.com"));
    form->fields = g_slist_append(form->fields, field1);

    FormField *field2 = _new_field();
    field2->var = strdup("var2");
    field2->type_t = FIELD_LIST_SINGLE;
    field2->values = g_slist_append(field2->values, strdup("value2"));
    form->fields = g_slist_append(form->fields, field2);

    gboolean ret = form_add_unique_value(form, "tag1", "me@server.com");

    int length = 0;
    int count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                if (g_strcmp0(curr_value->data, "me@server.com") == 0) {
                    count++;
                }
                curr_value = g_slist_next(curr_value);
            }
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(ret);
    assert_int_equal(length, 4);
    assert_int_equal(count, 1);

    form_destroy(form);
}

void add_value_adds_when_none(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    form->fields = g_slist_append(form->fields, field1);

    form_add_value(form, "tag1", "somevalue");

    int length = 0;
    char *value = NULL;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            value = field->values->data;
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_int_equal(length, 1);
    assert_string_equal(value, "somevalue");

    form_destroy(form);
}

void add_value_adds_when_some(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("some text"));
    field1->values = g_slist_append(field1->values, strdup("some more text"));
    field1->values = g_slist_append(field1->values, strdup("yet some more text"));
    form->fields = g_slist_append(form->fields, field1);

    form_add_value(form, "tag1", "new value");

    int num_values = 0;
    int new_value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                num_values++;
                if (g_strcmp0(curr_value->data, "new value") == 0) {
                    new_value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_int_equal(num_values, 4);
    assert_int_equal(new_value_count, 1);

    form_destroy(form);
}

void add_value_adds_when_exists(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("some text"));
    field1->values = g_slist_append(field1->values, strdup("some more text"));
    field1->values = g_slist_append(field1->values, strdup("yet some more text"));
    field1->values = g_slist_append(field1->values, strdup("new value"));
    form->fields = g_slist_append(form->fields, field1);

    form_add_value(form, "tag1", "new value");

    int num_values = 0;
    int new_value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                num_values++;
                if (g_strcmp0(curr_value->data, "new value") == 0) {
                    new_value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
            break;
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_int_equal(num_values, 5);
    assert_int_equal(new_value_count, 2);

    form_destroy(form);
}

void remove_value_does_nothing_when_none(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_value(form, "tag1", "some value");

    int length = -1;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_false(res);
    assert_int_equal(length, 0);

    form_destroy(form);
}

void remove_value_does_nothing_when_doesnt_exist(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    field1->values = g_slist_append(field1->values, strdup("value2"));
    field1->values = g_slist_append(field1->values, strdup("value3"));
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_value(form, "tag1", "value5");

    int length = -1;
    int value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                if (g_strcmp0(curr_value->data, "value5") == 0) {
                    value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_false(res);
    assert_int_equal(length, 4);
    assert_int_equal(value_count, 0);

    form_destroy(form);
}

void remove_value_removes_when_one(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_value(form, "tag1", "value4");

    int length = -1;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(res);
    assert_int_equal(length, 0);

    form_destroy(form);
}

void remove_value_removes_when_many(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    field1->values = g_slist_append(field1->values, strdup("value2"));
    field1->values = g_slist_append(field1->values, strdup("value3"));
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_value(form, "tag1", "value2");

    int length = -1;
    int value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                if (g_strcmp0(curr_value->data, "value2") == 0) {
                    value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(res);
    assert_int_equal(length, 3);
    assert_int_equal(value_count, 0);

    form_destroy(form);
}

void remove_text_multi_value_does_nothing_when_none(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_text_multi_value(form, "tag1", 3);

    int length = -1;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_false(res);
    assert_int_equal(length, 0);

    form_destroy(form);
}

void remove_text_multi_value_does_nothing_when_doesnt_exist(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    field1->values = g_slist_append(field1->values, strdup("value2"));
    field1->values = g_slist_append(field1->values, strdup("value3"));
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_text_multi_value(form, "tag1", 5);

    int length = -1;
    int value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                if (g_strcmp0(curr_value->data, "value5") == 0) {
                    value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_false(res);
    assert_int_equal(length, 4);
    assert_int_equal(value_count, 0);

    form_destroy(form);
}

void remove_text_multi_value_removes_when_one(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_text_multi_value(form, "tag1", 1);

    int length = -1;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(res);
    assert_int_equal(length, 0);

    form_destroy(form);
}

void remove_text_multi_value_removes_when_many(void **state)
{
    DataForm *form = _new_form();
    g_hash_table_insert(form->tag_to_var, strdup("tag1"), strdup("var1"));

    FormField *field1 = _new_field();
    field1->var = strdup("var1");
    field1->type_t = FIELD_LIST_MULTI;
    field1->values = g_slist_append(field1->values, strdup("value1"));
    field1->values = g_slist_append(field1->values, strdup("value2"));
    field1->values = g_slist_append(field1->values, strdup("value3"));
    field1->values = g_slist_append(field1->values, strdup("value4"));
    form->fields = g_slist_append(form->fields, field1);

    gboolean res = form_remove_text_multi_value(form, "tag1", 2);

    int length = -1;
    int value_count = 0;
    GSList *curr_field = form->fields;
    while (curr_field != NULL) {
        FormField *field = curr_field->data;
        if (g_strcmp0(field->var, "var1") == 0) {
            length = g_slist_length(field->values);
            GSList *curr_value = field->values;
            while (curr_value != NULL) {
                if (g_strcmp0(curr_value->data, "value2") == 0) {
                    value_count++;
                }
                curr_value = g_slist_next(curr_value);
            }
        }
        curr_field = g_slist_next(curr_field);
    }

    assert_true(res);
    assert_int_equal(length, 3);
    assert_int_equal(value_count, 0);

    form_destroy(form);
}