about summary refs log tree commit diff stats
path: root/tests/unittests/test_parser.c
blob: faefc9c7603a9969dd366d5fcb6a777e30e6963f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
= Abstract =

This text explains colorschemes and how they work.


= Context Tags =

Context Tags provide information about the context.  If the tag
"in_titlebar" is set, you probably want to know about the color
of a part of the titlebar now.

There are a number of context tags, specified in /ranger/gui/context.py
in the constant CONTEXT_KEYS.

A Context object, defined in the same file, contains attributes with
the names of all tags, whose values are either True or False.


= Implementation in the GUI Classes =

The class CursesShortcuts in the file /ranger/gui/curses_shortcuts.py
defines the methods color(*tags), color_at(y, x, wid, *tags) and
color_reset().  This class is a superclass of Displayable, so these
methods are available almost everywhere.

Something like color("in_titlebar", "directory") will be called to
get the color of directories in the titlebar.  This creates a
ranger.gui.context.Context object, sets its attributes "in_titlebar" and
"directory" to True, leaves the others as False, and passes it to the
colorscheme's use(context) method.


= The Color Scheme =

A colorscheme should be a subclass of ranger.gui.ColorScheme and
define the method use(context).  By looking at the context, this use-method
has to determine a 3-tuple of integers: (foreground, background, attribute)
and return it.

foreground and background are integers representing colors,
attribute is another integer with each bit representing one attribute.
These integers are interpreted by the used terminal emulator.

Abbreviations for colors and attributes are defined in ranger.gui.color.
Two attributes can be combined via logical OR: bold | reverse

Once the color for a set of tags is determined, it will be cached by
default.  If you want more dynamic colorschemes (such as a different
color for very large files), you will need to dig into the source code,
perhaps add an own tag and modify the draw-method of the widget to use
that tag.

Run tc_colorscheme to check if your colorschemes are valid.


= Specify a Colorscheme =

Colorschemes are searched for in these directories:
~/.ranger/colorschemes/
/ranger/colorschemes/

To specify which colorscheme to use, define the variable "colorscheme"
in your options.py:
colorscheme = colorschemes.default

This means, use the (one) colorscheme contained in
either ~/.ranger/colorschemes/default.py or /ranger/colorschemes/default.py.

You can define more than one colorscheme in a colorscheme file.  The
one named "Scheme" will be chosen in that case.  If there is no colorscheme
named "Scheme", an arbitrary one will be picked.  You could also explicitly
specify which colorscheme to use in your options.py:
colorscheme = colorschemes.default.MyOtherScheme


= Adapt a colorscheme =

You may want to adapt a colorscheme to your needs without having
a complete copy of it, but rather the changes only.  Say, you
want the exact same colors as in the default colorscheme, but
the directories to be green rather than blue, because you find the
blue hard to read.

This is done in the jungle colorscheme ranger.colorschemes.jungle.Scheme,
check it out for implementation details.  In short, I made a subclass
of the default scheme, set the initial colors to the result of the
default use() method and modified the colors how I wanted.

This has the obvious advantage that you need to write less, which
results in less maintainance work and a greater chance that your colorscheme
will work with future versions of ranger.
n360' href='#n360'>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
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdlib.h>

#include "tools/parser.h"

void
parse_null_returns_null(void **state)
{
    char *inp = NULL;
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_empty_returns_null(void **state)
{
    char *inp = "";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_space_returns_null(void **state)
{
    char *inp = "   ";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_cmd_no_args_returns_null(void **state)
{
    char *inp = "/cmd";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_cmd_with_space_returns_null(void **state)
{
    char *inp = "/cmd   ";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_cmd_with_too_few_returns_null(void **state)
{
    char *inp = "/cmd arg1";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 2, 3, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_cmd_with_too_many_returns_null(void **state)
{
    char *inp = "/cmd arg1 arg2 arg3 arg4";
    gboolean result = TRUE;
    gchar **args = parse_args(inp, 1, 3, &result);

    assert_false(result);
    assert_null(args);
    g_strfreev(args);
}

void
parse_cmd_one_arg(void **state)
{
    char *inp = "/cmd arg1";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_true(result);
    assert_int_equal(1, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    g_strfreev(args);
}

void
parse_cmd_two_args(void **state)
{
    char *inp = "/cmd arg1 arg2";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 1, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    g_strfreev(args);
}

void
parse_cmd_three_args(void **state)
{
    char *inp = "/cmd arg1 arg2 arg3";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("arg3", args[2]);
    g_strfreev(args);
}

void
parse_cmd_three_args_with_spaces(void **state)
{
    char *inp = "  /cmd    arg1  arg2     arg3 ";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("arg3", args[2]);
    g_strfreev(args);
}

void
parse_cmd_with_freetext(void **state)
{
    char *inp = "/cmd this is some free text";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 1, 1, &result);

    assert_true(result);
    assert_int_equal(1, g_strv_length(args));
    assert_string_equal("this is some free text", args[0]);
    g_strfreev(args);
}

void
parse_cmd_one_arg_with_freetext(void **state)
{
    char *inp = "/cmd arg1 this is some free text";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 1, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("this is some free text", args[1]);
    g_strfreev(args);
}

void
parse_cmd_two_args_with_freetext(void **state)
{
    char *inp = "/cmd arg1 arg2 this is some free text";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 1, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("this is some free text", args[2]);
    g_strfreev(args);
}

void
parse_cmd_min_zero(void **state)
{
    char *inp = "/cmd";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 0, 2, &result);

    assert_true(result);
    assert_int_equal(0, g_strv_length(args));
    assert_null(args[0]);
    g_strfreev(args);
}

void
parse_cmd_min_zero_with_freetext(void **state)
{
    char *inp = "/cmd";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 0, 2, &result);

    assert_true(result);
    assert_int_equal(0, g_strv_length(args));
    assert_null(args[0]);
    g_strfreev(args);
}

void
parse_cmd_with_quoted(void **state)
{
    char *inp = "/cmd \"arg1\" arg2";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 2, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    g_strfreev(args);
}

void
parse_cmd_with_quoted_and_space(void **state)
{
    char *inp = "/cmd \"the arg1\" arg2";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 2, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("the arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    g_strfreev(args);
}

void
parse_cmd_with_quoted_and_many_spaces(void **state)
{
    char *inp = "/cmd \"the arg1 is here\" arg2";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 2, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("the arg1 is here", args[0]);
    assert_string_equal("arg2", args[1]);
    g_strfreev(args);
}

void
parse_cmd_with_many_quoted_and_many_spaces(void **state)
{
    char *inp = "/cmd \"the arg1 is here\" \"and arg2 is right here\"";
    gboolean result = FALSE;
    gchar **args = parse_args(inp, 2, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("the arg1 is here", args[0]);
    assert_string_equal("and arg2 is right here", args[1]);
    g_strfreev(args);
}

void
parse_cmd_freetext_with_quoted(void **state)
{
    char *inp = "/cmd \"arg1\" arg2 hello there whats up";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("hello there whats up", args[2]);
    g_strfreev(args);
}

void
parse_cmd_freetext_with_quoted_and_space(void **state)
{
    char *inp = "/cmd \"the arg1\" arg2 another bit of freetext";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("the arg1", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("another bit of freetext", args[2]);
    g_strfreev(args);
}

void
parse_cmd_freetext_with_quoted_and_many_spaces(void **state)
{
    char *inp = "/cmd \"the arg1 is here\" arg2 some more freetext";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("the arg1 is here", args[0]);
    assert_string_equal("arg2", args[1]);
    assert_string_equal("some more freetext", args[2]);
    g_strfreev(args);
}

void
parse_cmd_freetext_with_many_quoted_and_many_spaces(void **state)
{
    char *inp = "/cmd \"the arg1 is here\" \"and arg2 is right here\" and heres the free text";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 3, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("the arg1 is here", args[0]);
    assert_string_equal("and arg2 is right here", args[1]);
    assert_string_equal("and heres the free text", args[2]);
    g_strfreev(args);
}

void
parse_cmd_with_quoted_freetext(void **state)
{
    char *inp = "/cmd arg1 here is \"some\" quoted freetext";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 1, 2, &result);

    assert_true(result);
    assert_int_equal(2, g_strv_length(args));
    assert_string_equal("arg1", args[0]);
    assert_string_equal("here is \"some\" quoted freetext", args[1]);
    g_strfreev(args);
}

void
parse_cmd_with_third_arg_quoted_0_min_3_max(void **state)
{
    char *inp = "/group add friends \"The User\"";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 0, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("add", args[0]);
    assert_string_equal("friends", args[1]);
    assert_string_equal("The User", args[2]);
}

void
parse_cmd_with_second_arg_quoted_0_min_3_max(void **state)
{
    char *inp = "/group add \"The Group\" friend";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 0, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("add", args[0]);
    assert_string_equal("The Group", args[1]);
    assert_string_equal("friend", args[2]);
}

void
parse_cmd_with_second_and_third_arg_quoted_0_min_3_max(void **state)
{
    char *inp = "/group add \"The Group\" \"The User\"";
    gboolean result = FALSE;
    gchar **args = parse_args_with_freetext(inp, 0, 3, &result);

    assert_true(result);
    assert_int_equal(3, g_strv_length(args));
    assert_string_equal("add", args[0]);
    assert_string_equal("The Group", args[1]);
    assert_string_equal("The User", args[2]);
}

void
count_one_token(void **state)
{
    char *inp = "one";
    int result = count_tokens(inp);

    assert_int_equal(1, result);
}

void
count_one_token_quoted_no_whitespace(void **state)
{
    char *inp = "\"one\"";
    int result = count_tokens(inp);

    assert_int_equal(1, result);
}

void
count_one_token_quoted_with_whitespace(void **state)
{
    char *inp = "\"one two\"";
    int result = count_tokens(inp);

    assert_int_equal(1, result);
}

void
count_two_tokens(void **state)
{
    char *inp = "one two";
    int result = count_tokens(inp);

    assert_int_equal(2, result);
}

void
count_two_tokens_first_quoted(void **state)
{
    char *inp = "\"one and\" two";
    int result = count_tokens(inp);

    assert_int_equal(2, result);
}

void
count_two_tokens_second_quoted(void **state)
{
    char *inp = "one \"two and\"";
    int result = count_tokens(inp);

    assert_int_equal(2, result);
}

void
count_two_tokens_both_quoted(void **state)
{
    char *inp = "\"one and then\" \"two and\"";
    int result = count_tokens(inp);

    assert_int_equal(2, result);
}

void
get_first_of_one(void **state)
{
    char *inp = "one";
    char *result = get_start(inp, 2);

    assert_string_equal("one", result);
}

void
get_first_of_two(void **state)
{
    char *inp = "one two";
    char *result = get_start(inp, 2);

    assert_string_equal("one ", result);
}

void
get_first_two_of_three(void **state)
{
    char *inp = "one two three";
    char *result = get_start(inp, 3);

    assert_string_equal("one two ", result);
}

void
get_first_two_of_three_first_quoted(void **state)
{
    char *inp = "\"one\" two three";
    char *result = get_start(inp, 3);

    assert_string_equal("\"one\" two ", result);
}

void
get_first_two_of_three_second_quoted(void **state)
{
    char *inp = "one \"two\" three";
    char *result = get_start(inp, 3);

    assert_string_equal("one \"two\" ", result);
}

void
get_first_two_of_three_first_and_second_quoted(void **state)
{
    char *inp = "\"one\" \"two\" three";
    char *result = get_start(inp, 3);

    assert_string_equal("\"one\" \"two\" ", result);
}

void
parse_options_when_none_returns_empty_hasmap(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", NULL };
    gchar *keys[] = { "opt1", NULL };

    gboolean res = FALSE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_true(options != NULL);
    assert_int_equal(0, g_hash_table_size(options));
    assert_true(res);

    options_destroy(options);
}

void
parse_options_when_opt1_no_val_sets_error(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", NULL };
    gchar *keys[] = { "opt1", NULL };

    gboolean res = TRUE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_null(options);
    assert_false(res);

    options_destroy(options);
}

void
parse_options_when_one_returns_map(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", NULL };
    gchar *keys[] = { "opt1", NULL };

    gboolean res = FALSE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_int_equal(1, g_hash_table_size(options));
    assert_true(g_hash_table_contains(options, "opt1"));
    assert_string_equal("val1", g_hash_table_lookup(options, "opt1"));
    assert_true(res);

    options_destroy(options);
}

void
parse_options_when_opt2_no_val_sets_error(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "opt2", NULL };
    gchar *keys[] = { "opt1", "opt2", NULL };

    gboolean res = TRUE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_null(options);
    assert_false(res);

    options_destroy(options);
}

void
parse_options_when_two_returns_map(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "opt2", "val2", NULL };
    gchar *keys[] = { "opt1", "opt2", NULL };

    gboolean res = FALSE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_int_equal(2, g_hash_table_size(options));
    assert_true(g_hash_table_contains(options, "opt1"));
    assert_true(g_hash_table_contains(options, "opt2"));
    assert_string_equal("val1", g_hash_table_lookup(options, "opt1"));
    assert_string_equal("val2", g_hash_table_lookup(options, "opt2"));
    assert_true(res);

    options_destroy(options);
}

void
parse_options_when_opt3_no_val_sets_error(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "opt2", "val2", "opt3", NULL };
    gchar *keys[] = { "opt1", "opt2", "opt3", NULL };

    gboolean res = TRUE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_null(options);
    assert_false(res);

    options_destroy(options);
}

void
parse_options_when_three_returns_map(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "opt2", "val2", "opt3", "val3", NULL };
    gchar *keys[] = { "opt1", "opt2", "opt3", NULL };

    gboolean res = FALSE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_int_equal(3, g_hash_table_size(options));
    assert_true(g_hash_table_contains(options, "opt1"));
    assert_true(g_hash_table_contains(options, "opt2"));
    assert_true(g_hash_table_contains(options, "opt3"));
    assert_string_equal("val1", g_hash_table_lookup(options, "opt1"));
    assert_string_equal("val2", g_hash_table_lookup(options, "opt2"));
    assert_string_equal("val3", g_hash_table_lookup(options, "opt3"));
    assert_true(res);

    options_destroy(options);
}

void
parse_options_when_unknown_opt_sets_error(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "oops", "val2", "opt3", "val3", NULL };
    gchar *keys[] = { "opt1", "opt2", "opt3", NULL };

    gboolean res = TRUE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_null(options);
    assert_false(res);

    options_destroy(options);
}

void
parse_options_with_duplicated_option_sets_error(void **state)
{
    gchar *args[] = { "cmd1", "cmd2", "opt1", "val1", "opt2", "val2", "opt1", "val3", NULL };
    gchar *keys[] = { "opt1", "opt2", "opt3", NULL };

    gboolean res = TRUE;

    GHashTable *options = parse_options(&args[2], keys, &res);

    assert_null(options);
    assert_false(res);

    options_destroy(options);
}