<commit_before>#!perl
# vim:ts=4:sw=4:expandtab
#
# Tests the standalone parser binary to see if it calls the right code when
# confronted with various commands, if it prints proper error messages for
# wrong commands and if it terminates in every case.
#
use i3test i3_autostart => 0;

sub parser_calls {
    my ($command) = @_;

    # TODO: use a timeout, so that we can error out if it doesn’t terminate
    # TODO: better way of passing arguments
    my $stdout = qx(../test.commands_parser '$command' 2>&-);

    # Filter out all debugging output.
    my @lines = split("\n", $stdout);
    @lines = grep { not /^# / } @lines;

    # The criteria management calls are irrelevant and not what we want to test
    # in the first place.
    @lines = grep { !(/cmd_criteria_init()/ || /cmd_criteria_match_windows/) } @lines;
    return join("\n", @lines);
}

################################################################################
# 1: First that the parser properly recognizes commands which are ok.
################################################################################

is(parser_calls('move workspace 3'),
   'cmd_move_con_to_workspace_name(3)',
   'single number (move workspace 3) ok');

is(parser_calls('move to workspace 3'),
   'cmd_move_con_to_workspace_name(3)',
   'to (move to workspace 3) ok');

is(parser_calls('move window to workspace 3'),
   'cmd_move_con_to_workspace_name(3)',
   'window to (move window to workspace 3) ok');

is(parser_calls('move container to workspace 3'),
   'cmd_move_con_to_workspace_name(3)',
   'container to (move container to workspace 3) ok');

is(parser_calls('move workspace foobar'),
   'cmd_move_con_to_workspace_name(foobar)',
   'single word (move workspace foobar) ok');

is(parser_calls('move workspace 3: foobar'),
   'cmd_move_con_to_workspace_name(3: foobar)',
   'multiple words (move workspace 3: foobar) ok');

is(parser_calls('move workspace "3: foobar"'),
   'cmd_move_con_to_workspace_name(3: foobar)',
   'double quotes (move workspace "3: foobar") ok');

is(parser_calls('move workspace "3: foobar, baz"'),
   'cmd_move_con_to_workspace_name(3: foobar, baz)',
   'quotes with comma (move workspace "3: foobar, baz") ok');

is(parser_calls('move workspace 3: foobar, nop foo'),
   "cmd_move_con_to_workspace_name(3: foobar)\n" .
   "cmd_nop(foo)",
   'multiple ops (move workspace 3: foobar, nop foo) ok');

is(parser_calls('exec i3-sensible-terminal'),
   'cmd_exec((null), i3-sensible-terminal)',
   'exec ok');

is(parser_calls('exec --no-startup-id i3-sensible-terminal'),
   'cmd_exec(--no-startup-id, i3-sensible-terminal)',
   'exec --no-startup-id ok');

is(parser_calls('resize shrink left'),
   'cmd_resize(shrink, left, 10, 10)',
   'simple resize ok');

is(parser_calls('resize shrink left 25 px'),
   'cmd_resize(shrink, left, 25, 10)',
   'px resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt'),
   'cmd_resize(shrink, left, 25, 33)',
   'px + ppt resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt'),
   'cmd_resize(shrink, left, 25, 33)',
   'px + ppt resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt,'),
   'cmd_resize(shrink, left, 25, 33)',
   'trailing comma resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt;'),
   'cmd_resize(shrink, left, 25, 33)',
   'trailing semicolon resize ok');

is(parser_calls('resize shrink left 25'),
   'cmd_resize(shrink, left, 25, 10)',
   'resize early end ok');

is(parser_calls('[con_mark=yay] focus'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay con_mark=bar] focus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay\tcon_mark=bar] focus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay\tcon_mark=bar]\tfocus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls('[con_mark="yay"] focus'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus()",
   'quoted criteria focus ok');

# Make sure trailing whitespace is stripped off: While this is not an issue for
# commands being parsed due to the configuration, people might send IPC
# commands with leading or trailing newlines.
is(parser_calls("workspace test\n"),
   'cmd_workspace_name(test)',
   'trailing whitespace stripped off ok');

is(parser_calls("\nworkspace test"),
   'cmd_workspace_name(test)',
   'trailing whitespace stripped off ok');

################################################################################
# 2: Verify that the parser spits out the right error message on commands which
# are not ok.
################################################################################

is(parser_calls('unknown_literal'),
   "Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'nop', 'scratchpad', 'mode'\n" .
   "Your command: unknown_literal\n" .
   "              ^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

is(parser_calls('move something to somewhere'),
   "Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down'\n" .
   "Your command: move something to somewhere\n" .
   "                   ^^^^^^^^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

################################################################################
# 3: Verify that escaping of double quotes works correctly
################################################################################

is(parser_calls('workspace "foo"'),
   'cmd_workspace_name(foo)',
   'Command with simple double quotes ok');

is(parser_calls('workspace "foo'),
   'cmd_workspace_name(foo)',
   'Command without ending double quotes ok');

is(parser_calls('workspace "foo \"bar"'),
   'cmd_workspace_name(foo "bar)',
   'Command with escaped double quotes ok');

done_testing;
<commit_msg>t/187-commands-parser: shave off a bit of time by consolidating calls<commit_after>#!perl
# vim:ts=4:sw=4:expandtab
#
# Tests the standalone parser binary to see if it calls the right code when
# confronted with various commands, if it prints proper error messages for
# wrong commands and if it terminates in every case.
#
use i3test i3_autostart => 0;

sub parser_calls {
    my ($command) = @_;

    # TODO: use a timeout, so that we can error out if it doesn’t terminate
    # TODO: better way of passing arguments
    my $stdout = qx(../test.commands_parser '$command' 2>&-);

    # Filter out all debugging output.
    my @lines = split("\n", $stdout);
    @lines = grep { not /^# / } @lines;

    # The criteria management calls are irrelevant and not what we want to test
    # in the first place.
    @lines = grep { !(/cmd_criteria_init()/ || /cmd_criteria_match_windows/) } @lines;
    return join("\n", @lines);
}

################################################################################
# 1: First that the parser properly recognizes commands which are ok.
################################################################################

# The first call has only a single command, the following ones are consolidated
# for performance.
is(parser_calls('move workspace 3'),
   'cmd_move_con_to_workspace_name(3)',
   'single number (move workspace 3) ok');

is(parser_calls(
   'move to workspace 3; ' .
   'move window to workspace 3; ' .
   'move container to workspace 3; ' .
   'move workspace foobar; ' .
   'move workspace 3: foobar; ' .
   'move workspace "3: foobar"; ' .
   'move workspace "3: foobar, baz"; '),
   "cmd_move_con_to_workspace_name(3)\n" .
   "cmd_move_con_to_workspace_name(3)\n" .
   "cmd_move_con_to_workspace_name(3)\n" .
   "cmd_move_con_to_workspace_name(foobar)\n" .
   "cmd_move_con_to_workspace_name(3: foobar)\n" .
   "cmd_move_con_to_workspace_name(3: foobar)\n" .
   "cmd_move_con_to_workspace_name(3: foobar, baz)",
   'move ok');

is(parser_calls('move workspace 3: foobar, nop foo'),
   "cmd_move_con_to_workspace_name(3: foobar)\n" .
   "cmd_nop(foo)",
   'multiple ops (move workspace 3: foobar, nop foo) ok');

is(parser_calls(
   'exec i3-sensible-terminal; ' .
   'exec --no-startup-id i3-sensible-terminal'),
   "cmd_exec((null), i3-sensible-terminal)\n" .
   "cmd_exec(--no-startup-id, i3-sensible-terminal)",
   'exec ok');

is(parser_calls(
   'resize shrink left; ' .
   'resize shrink left 25 px; ' .
   'resize shrink left 25 px or 33 ppt; ' .
   'resize shrink left 25'),
   "cmd_resize(shrink, left, 10, 10)\n" .
   "cmd_resize(shrink, left, 25, 10)\n" .
   "cmd_resize(shrink, left, 25, 33)\n" .
   "cmd_resize(shrink, left, 25, 10)",
   'simple resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt,'),
   'cmd_resize(shrink, left, 25, 33)',
   'trailing comma resize ok');

is(parser_calls('resize shrink left 25 px or 33 ppt;'),
   'cmd_resize(shrink, left, 25, 33)',
   'trailing semicolon resize ok');

is(parser_calls('[con_mark=yay] focus'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay con_mark=bar] focus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay\tcon_mark=bar] focus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls("[con_mark=yay\tcon_mark=bar]\tfocus"),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_criteria_add(con_mark, bar)\n" .
   "cmd_focus()",
   'criteria focus ok');

is(parser_calls('[con_mark="yay"] focus'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus()",
   'quoted criteria focus ok');

# Make sure trailing whitespace is stripped off: While this is not an issue for
# commands being parsed due to the configuration, people might send IPC
# commands with leading or trailing newlines.
is(parser_calls("workspace test\n"),
   'cmd_workspace_name(test)',
   'trailing whitespace stripped off ok');

is(parser_calls("\nworkspace test"),
   'cmd_workspace_name(test)',
   'trailing whitespace stripped off ok');

################################################################################
# 2: Verify that the parser spits out the right error message on commands which
# are not ok.
################################################################################

is(parser_calls('unknown_literal'),
   "Expected one of these tokens: <end>, '[', 'move', 'exec', 'exit', 'restart', 'reload', 'border', 'layout', 'append_layout', 'workspace', 'focus', 'kill', 'open', 'fullscreen', 'split', 'floating', 'mark', 'resize', 'nop', 'scratchpad', 'mode'\n" .
   "Your command: unknown_literal\n" .
   "              ^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

is(parser_calls('move something to somewhere'),
   "Expected one of these tokens: 'window', 'container', 'to', 'workspace', 'output', 'scratchpad', 'left', 'right', 'up', 'down'\n" .
   "Your command: move something to somewhere\n" .
   "                   ^^^^^^^^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

################################################################################
# 3: Verify that escaping of double quotes works correctly
################################################################################

is(parser_calls('workspace "foo"'),
   'cmd_workspace_name(foo)',
   'Command with simple double quotes ok');

is(parser_calls('workspace "foo'),
   'cmd_workspace_name(foo)',
   'Command without ending double quotes ok');

is(parser_calls('workspace "foo \"bar"'),
   'cmd_workspace_name(foo "bar)',
   'Command with escaped double quotes ok');

done_testing;

{'fraction_non_alphanumeric': 0.2220838522208385, 'fraction_numerical': 0.013200498132004981, 'mean_word_length': 4.853255587949466, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5755435', 'n_tokens_mistral': 4107, 'n_tokens_neox': 3920, 'n_words': 1078}