1#!perl
2# vim:ts=4:sw=4:expandtab
3#
4# Please read the following documents before working on tests:
5# • https://build.i3wm.org/docs/testsuite.html
6#   (or docs/testsuite)
7#
8# • https://build.i3wm.org/docs/lib-i3test.html
9#   (alternatively: perldoc ./testcases/lib/i3test.pm)
10#
11# • https://build.i3wm.org/docs/ipc.html
12#   (or docs/ipc)
13#
14# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
15#   (unless you are already familiar with Perl)
16#
17# Checks if the 'move [window/container] to workspace' command works correctly
18#
19use i3test;
20
21my $i3 = i3(get_socket_path());
22
23# We move the pointer out of our way to avoid a bug where the focus will
24# be set to the window under the cursor
25sync_with_i3;
26$x->root->warp_pointer(0, 0);
27sync_with_i3;
28
29sub move_workspace_test {
30    my ($movecmd) = @_;
31
32    my $tmp = get_unused_workspace();
33    my $tmp2 = get_unused_workspace();
34    cmd "workspace $tmp";
35
36    is_num_children($tmp, 0, 'no containers yet');
37
38    my $first = open_empty_con($i3);
39    my $second = open_empty_con($i3);
40    is_num_children($tmp, 2, 'two containers on first ws');
41
42    cmd "workspace $tmp2";
43    is_num_children($tmp2, 0, 'no containers on second ws yet');
44
45    cmd "workspace $tmp";
46
47    cmd "$movecmd $tmp2";
48    is_num_children($tmp, 1, 'one container on first ws anymore');
49    is_num_children($tmp2, 1, 'one container on second ws');
50    my ($nodes, $focus) = get_ws_content($tmp2);
51
52    is($focus->[0], $second, 'same container on different ws');
53
54    ($nodes, $focus) = get_ws_content($tmp);
55    ok($nodes->[0]->{focused}, 'first container focused on first ws');
56}
57
58move_workspace_test('move workspace');  # supported for legacy reasons
59move_workspace_test('move to workspace');
60# Those are just synonyms and more verbose ways of saying the same thing:
61move_workspace_test('move window to workspace');
62move_workspace_test('move container to workspace');
63
64################################################################################
65# Check that 'move to workspace number <number>' works to move a window to
66# named workspaces which start with <number>.
67################################################################################
68
69cmd 'workspace 13: meh';
70cmd 'open';
71is_num_children('13: meh', 1, 'one container on 13: meh');
72
73ok(!workspace_exists('13'), 'workspace 13 does not exist yet');
74
75cmd 'workspace 12';
76cmd 'open';
77
78cmd 'move to workspace number 13';
79is_num_children('13: meh', 2, 'one container on 13: meh');
80is_num_children('12', 0, 'no container on 12 anymore');
81
82ok(!workspace_exists('13'), 'workspace 13 does still not exist');
83
84################################################################################
85# Check that 'move to workspace number <number><name>' works to move a window to
86# named workspaces which start with <number>.
87################################################################################
88
89cmd 'workspace 15: meh';
90cmd 'open';
91is_num_children('15: meh', 1, 'one container on 15: meh');
92
93ok(!workspace_exists('15'), 'workspace 15 does not exist yet');
94ok(!workspace_exists('15: duh'), 'workspace 15: duh does not exist yet');
95
96cmd 'workspace 14';
97cmd 'open';
98
99cmd 'move to workspace number 15: duh';
100is_num_children('15: meh', 2, 'two containers on 15: meh');
101is_num_children('14', 0, 'no container on 14 anymore');
102
103ok(!workspace_exists('15'), 'workspace 15 does still not exist');
104ok(!workspace_exists('15: duh'), 'workspace 15 does still not exist');
105
106###################################################################
107# check if 'move workspace next' and 'move workspace prev' work
108###################################################################
109
110# Open two containers on the first workspace, one container on the second
111# workspace. Because the workspaces are named, they will be sorted by order of
112# creation.
113my $tmp = get_unused_workspace();
114my $tmp2 = get_unused_workspace();
115cmd "workspace $tmp";
116is_num_children($tmp, 0, 'no containers yet');
117my $first = open_empty_con($i3);
118my $second = open_empty_con($i3);
119is_num_children($tmp, 2, 'two containers');
120
121cmd "workspace $tmp2";
122is_num_children($tmp2, 0, 'no containers yet');
123my $third = open_empty_con($i3);
124is_num_children($tmp2, 1, 'one container on second ws');
125
126# go back to the first workspace, move one of the containers to the next one
127cmd "workspace $tmp";
128cmd 'move workspace next';
129is_num_children($tmp, 1, 'one container on first ws');
130is_num_children($tmp2, 2, 'two containers on second ws');
131
132# go to the second workspace and move two containers to the first one
133cmd "workspace $tmp2";
134cmd 'move workspace prev';
135cmd 'move workspace prev';
136is_num_children($tmp, 3, 'three containers on first ws');
137is_num_children($tmp2, 0, 'no containers on second ws');
138
139###################################################################
140# check if 'move workspace current' works
141###################################################################
142
143$tmp = get_unused_workspace();
144$tmp2 = get_unused_workspace();
145
146cmd "workspace $tmp";
147$first = open_window(name => 'win-name');
148is_num_children($tmp, 1, 'one container on first ws');
149
150cmd "workspace $tmp2";
151is_num_children($tmp2, 0, 'no containers yet');
152
153cmd qq|[title="win-name"] move workspace $tmp2|;
154is_num_children($tmp2, 1, 'one container on second ws');
155
156cmd qq|[title="win-name"] move workspace $tmp|;
157is_num_children($tmp2, 0, 'no containers on second ws');
158
159###################################################################
160# check if floating cons are moved to new workspaces properly
161# (that is, if they are floating on the target ws, too)
162###################################################################
163
164$tmp = get_unused_workspace();
165$tmp2 = get_unused_workspace();
166cmd "workspace $tmp";
167
168cmd "open";
169cmd "floating toggle";
170
171my $ws = get_ws($tmp);
172is(@{$ws->{nodes}}, 0, 'no nodes on workspace');
173is(@{$ws->{floating_nodes}}, 1, 'one floating node on workspace');
174
175cmd "move workspace $tmp2";
176
177$ws = get_ws($tmp2);
178is(@{$ws->{nodes}}, 0, 'no nodes on workspace');
179is(@{$ws->{floating_nodes}}, 1, 'one floating node on workspace');
180
181################################################################################
182# Check that 'move workspace number' works correctly.
183################################################################################
184
185$tmp = get_unused_workspace();
186cmd 'open';
187
188cmd 'workspace 16';
189cmd 'open';
190is_num_children('16', 1, 'one node on ws 16');
191
192cmd "workspace $tmp";
193cmd 'open';
194cmd 'move workspace number 16';
195is_num_children('16', 2, 'two nodes on ws 16');
196
197ok(!workspace_exists('17'), 'workspace 17 does not exist yet');
198cmd 'open';
199cmd 'move workspace number 17';
200ok(workspace_exists('17'), 'workspace 17 created by moving');
201is(@{get_ws('17')->{nodes}}, 1, 'one node on ws 16');
202
203################################################################################
204# The following four tests verify the various 'move workspace' commands when
205# the selection is itself a workspace.
206################################################################################
207
208# borrowed from 122-split.t
209# recursively sums up all nodes and their children
210sub sum_nodes {
211    my ($nodes) = @_;
212
213    return 0 if !@{$nodes};
214
215    my @children = (map { @{$_->{nodes}} } @{$nodes},
216                    map { @{$_->{'floating_nodes'}} } @{$nodes});
217
218    return @{$nodes} + sum_nodes(\@children);
219}
220
221############################################################
222# move workspace 'next|prev'
223############################################################
224$tmp = get_unused_workspace();
225$tmp2 = get_unused_workspace();
226
227cmd "workspace $tmp";
228cmd 'open';
229is_num_children($tmp, 1, 'one container on first ws');
230
231cmd "workspace $tmp2";
232cmd 'open';
233is_num_children($tmp2, 1, 'one container on second ws');
234cmd 'open';
235is_num_children($tmp2, 2, 'two containers on second ws');
236
237cmd 'focus parent';
238cmd 'move workspace prev';
239
240is_num_children($tmp, 2, 'two child containers on first ws');
241is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
242is_num_children($tmp2, 0, 'no containers on second ws');
243
244############################################################
245# move workspace current
246# This is a special case that should be a no-op.
247############################################################
248$tmp = fresh_workspace();
249
250cmd 'open';
251is_num_children($tmp, 1, 'one container on first ws');
252my $tmpcount = sum_nodes(get_ws_content($tmp));
253
254cmd 'focus parent';
255cmd "move workspace $tmp";
256
257is(sum_nodes(get_ws_content($tmp)), $tmpcount, 'number of containers in first ws unchanged');
258
259############################################################
260# move workspace '<name>'
261############################################################
262$tmp2 = get_unused_workspace();
263$tmp = fresh_workspace();
264
265cmd 'open';
266is_num_children($tmp, 1, 'one container on first ws');
267
268cmd "workspace $tmp2";
269cmd 'open';
270is_num_children($tmp2, 1, 'one container on second ws');
271cmd 'open';
272is_num_children($tmp2, 2, 'two containers on second ws');
273
274cmd 'focus parent';
275cmd "move workspace $tmp";
276
277is_num_children($tmp, 2, 'two child containers on first ws');
278is(sum_nodes(get_ws_content($tmp)), 4, 'four total containers on first ws');
279is_num_children($tmp2, 0, 'no containers on second ws');
280
281############################################################
282# move workspace number '<number>'
283############################################################
284cmd 'workspace 18';
285cmd 'open';
286is_num_children('18', 1, 'one container on ws 18');
287
288cmd 'workspace 19';
289cmd 'open';
290is_num_children('19', 1, 'one container on ws 19');
291cmd 'open';
292is_num_children('19', 2, 'two containers on ws 19');
293
294cmd 'focus parent';
295cmd 'move workspace number 18';
296
297is_num_children('18', 2, 'two child containers on ws 18');
298is(sum_nodes(get_ws_content('18')), 4, 'four total containers on ws 18');
299is_num_children('19', 0, 'no containers on ws 19');
300
301###################################################################
302# move workspace '<name>' with a floating child
303###################################################################
304$tmp2 = get_unused_workspace();
305$tmp = fresh_workspace();
306cmd 'open';
307cmd 'floating toggle';
308cmd 'open';
309cmd 'floating toggle';
310cmd 'open';
311
312$ws = get_ws($tmp);
313is_num_children($tmp, 1, 'one container on first workspace');
314is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on first workspace');
315
316cmd 'focus parent';
317cmd "move workspace $tmp2";
318
319$ws = get_ws($tmp2);
320is_num_children($tmp2, 1, 'one container on second workspace');
321is(@{$ws->{floating_nodes}}, 2, 'two floating nodes on second workspace');
322
323###################################################################
324# same as the above, but with only floating children
325###################################################################
326$tmp2 = get_unused_workspace();
327$tmp = fresh_workspace();
328cmd 'open';
329cmd 'floating toggle';
330
331$ws = get_ws($tmp);
332is_num_children($tmp, 0, 'no regular nodes on first workspace');
333is(@{$ws->{floating_nodes}}, 1, 'one floating node on first workspace');
334
335cmd 'focus parent';
336cmd "move workspace $tmp2";
337
338$ws = get_ws($tmp2);
339is_num_children($tmp2, 0, 'no regular nodes on second workspace');
340is(@{$ws->{floating_nodes}}, 1, 'one floating node on second workspace');
341
342###################################################################
343# Test that when moving a fullscreen floating window to a workspace
344# that already has an other fullscreen container, the second
345# container gets un-fullscreened.
346# See #4124
347###################################################################
348$tmp2 = fresh_workspace;
349$second = open_window;
350cmd 'fullscreen enable';
351$ws = get_ws($tmp2);
352is($ws->{nodes}->[0]->{fullscreen_mode}, 1, 'sanity check: fullscreen enabled');
353
354$tmp = fresh_workspace;
355$first = open_window;
356cmd 'floating enable, fullscreen enable';
357cmd "move workspace $tmp2";
358
359$ws = get_ws($tmp2);
360is_num_children($tmp2, 1, 'one regular node on second workspace');
361is_num_fullscreen($tmp2, 1, 'one fullscreen node on second workspace');
362is(@{$ws->{floating_nodes}}, 1, 'one floating node on second workspace');
363is($ws->{nodes}->[0]->{fullscreen_mode}, 0, 'previous fullscreen disabled');
364
365###################################################################
366# Same as above, but trigger the bug with the parent of a
367# fullscreen container, instead of a CT_FLOATING_CON.
368###################################################################
369$tmp2 = fresh_workspace;
370$second = open_window;
371cmd 'fullscreen enable';
372$ws = get_ws($tmp2);
373is($ws->{nodes}->[0]->{fullscreen_mode}, 1, 'sanity check: fullscreen enabled');
374
375$tmp = fresh_workspace;
376open_window;
377$first = open_window;
378cmd 'layout tabbed';
379cmd 'focus parent, mark a, focus child';
380cmd 'fullscreen enable';
381cmd "[con_mark=a] move workspace $tmp2";
382
383$ws = get_ws($tmp2);
384is(sum_nodes(get_ws_content($tmp2)), 4, '3 leafs & 1 split node in second workspace');
385# is_num_fullscreen does not catch this nested fullscreen container
386is($ws->{nodes}->[0]->{fullscreen_mode}, 0, 'previous fullscreen disabled');
387is($ws->{nodes}->[1]->{nodes}->[1]->{fullscreen_mode}, 1, 'nested fullscreen from moved container preserved');
388
389###################################################################
390# Check that moving an empty workspace using criteria doesn't
391# create unfocused empty workspace.
392###################################################################
393$tmp2 = get_unused_workspace();
394$tmp = fresh_workspace();
395cmd 'mark a';
396cmd "[con_mark=a] move to workspace $tmp2";
397
398is (get_ws($tmp2), undef, 'No empty workspace created');
399
400done_testing;
401