aboutsummaryrefslogtreecommitdiff
path: root/testcases/t/187-commands-parser.t
blob: c2ff4a26e33cf8680bc06a8449a5c55500b1601e (plain)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!perl
# vim:ts=4:sw=4:expandtab
#
# Please read the following documents before working on tests:
# • https://build.i3wm.org/docs/testsuite.html
#   (or docs/testsuite)
#
# • https://build.i3wm.org/docs/lib-i3test.html
#   (alternatively: perldoc ./testcases/lib/i3test.pm)
#
# • https://build.i3wm.org/docs/ipc.html
#   (or docs/ipc)
#
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
#   (unless you are already familiar with Perl)
#
# 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>&1 >&-);

    # 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, (null))',
   '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 torrent; ' .
   'move workspace to output LVDS1; ' .
   'move to output LVDS1 DVI1; ' .
   'move workspace 3: foobar; ' .
   'move workspace "3: foobar"; ' .
   'move workspace "3: foobar, baz"; '),
   "cmd_move_con_to_workspace_name(3, (null))\n" .
   "cmd_move_con_to_workspace_name(3, (null))\n" .
   "cmd_move_con_to_workspace_name(3, (null))\n" .
   "cmd_move_con_to_workspace_name(foobar, (null))\n" .
   "cmd_move_con_to_workspace_name(torrent, (null))\n" .
   "cmd_move_con_to_output(LVDS1, 1)\n" .
   "cmd_move_con_to_output(NULL, 1)\n" .
   "cmd_move_con_to_output(LVDS1, 0)\n" .
   "cmd_move_con_to_output(DVI1, 0)\n" .
   "cmd_move_con_to_output(NULL, 0)\n" .
   "cmd_move_con_to_workspace_name(3: foobar, (null))\n" .
   "cmd_move_con_to_workspace_name(3: foobar, (null))\n" .
   "cmd_move_con_to_workspace_name(3: foobar, baz, (null))",
   'move ok');

is(parser_calls('move workspace 3: foobar, nop foo'),
   "cmd_move_con_to_workspace_name(3: foobar, (null))\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, 0)\n" .
   "cmd_resize(shrink, left, 25, 33)\n" .
   "cmd_resize(shrink, left, 25, 0)",
   '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(0)",
   'criteria focus ok');

is(parser_calls('[con_mark=yay] focus workspace'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus(1)",
   'criteria focus workspace 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(0)",
   '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(0)",
   '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(0)",
   'criteria focus ok');

is(parser_calls('[con_mark="yay"] focus'),
   "cmd_criteria_add(con_mark, yay)\n" .
   "cmd_focus(0)",
   '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, (null))',
   'trailing whitespace stripped off ok');

is(parser_calls("\nworkspace test"),
   'cmd_workspace_name(test, (null))',
   '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'),
   "ERROR: Expected one of these tokens: <end>, '[', '" . join("', '", qw(
       move
       exec
       exit
       restart
       reload
       shmlog
       debuglog
       border
       layout
       append_layout
       workspace
       focus
       kill
       open
       fullscreen
       sticky
       split
       floating
       mark
       unmark
       resize
       rename
       nop
       scratchpad
       swap
       title_format
       title_window_icon
       mode
       bar
       gaps
    )) . "'\n" .
   "ERROR: Your command: unknown_literal\n" .
   "ERROR:               ^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

is(parser_calls('move something to somewhere'),
   "ERROR: Expected one of these tokens: 'window', 'container', 'to', '--no-auto-back-and-forth', 'workspace', 'output', 'mark', 'scratchpad', 'left', 'right', 'up', 'down', 'position', 'absolute'\n" .
   "ERROR: Your command: move something to somewhere\n" .
   "ERROR:                    ^^^^^^^^^^^^^^^^^^^^^^",
   'error for unknown literal ok');

################################################################################
# 3: Verify that escaping works correctly
################################################################################

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

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

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

is(parser_calls('workspace "foo \\'),
   'cmd_workspace_name(foo \\, (null))',
   'Command with single backslash in the end ok');

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

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

################################################################################
# 4: Verify that resize commands with a "px or ppt"-construction are parsed
# correctly
################################################################################

is(parser_calls("resize shrink width 10 px or"),
   "ERROR: Expected one of these tokens: <number>\n" .
   "ERROR: Your command: resize shrink width 10 px or\n" .
   "ERROR:                                           ",
   "error for resize command with incomplete 'or'-construction ok");

is(parser_calls("resize grow left 10 px or 20 ppt"),
   "cmd_resize(grow, left, 10, 20)",
   "resize command with 'or'-construction ok");

done_testing;