Files
i3/testcases/t/243-move-to-mark.t
Albert Safin beb96ad18c Move container to marked workspace: refine corner case
This commit should fix "move con to parent" trick (see below) in the
case when con->parent->parent is a workspace.

The trick:

    mark _a, focus parent, focus parent, mark _b,
    [con_mark=_a] move window to mark _b, [con_mark=_a] focus

The trick got broken in commit 626af81232
in order to fix an i3 crash ().  Reverting said commit fixes the
trick.  The crash is caused by the fact that empty workspace isn't
considered a split (checked in src/con.c:1324), so the moved window ends
up as a sibling of the target workspace, not as its child.
2019-10-07 18:15:03 +00:00

432 lines
14 KiB
Perl

#!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 for the 'move [window|container] to mark' command
# Ticket: #1643
use i3test;
# In the following tests descriptions, we will always use the following names:
# * 'S' for the source container which is going to be moved,
# * 'M' for the marked target container to which 'S' will be moved.
my ($A, $B, $S, $M, $F, $source_ws, $target_ws, $ws);
my ($nodes, $focus);
my $__i3_scratch;
my $cmd_result;
my $_NET_WM_STATE_REMOVE = 0;
my $_NET_WM_STATE_ADD = 1;
my $_NET_WM_STATE_TOGGLE = 2;
sub set_urgency {
my ($win, $urgent_flag) = @_;
my $msg = pack "CCSLLLLLL",
X11::XCB::CLIENT_MESSAGE, # response_type
32, # format
0, # sequence
$win->id, # window
$x->atom(name => '_NET_WM_STATE')->id, # message type
($urgent_flag ? $_NET_WM_STATE_ADD : $_NET_WM_STATE_REMOVE), # data32[0]
$x->atom(name => '_NET_WM_STATE_DEMANDS_ATTENTION')->id, # data32[1]
0, # data32[2]
0, # data32[3]
0; # data32[4]
$x->send_event(0, $x->get_root_window(), X11::XCB::EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);
}
###############################################################################
# Given 'M' and 'S' in a horizontal split, when 'S' is moved to 'M', then
# verify that nothing changed.
###############################################################################
$ws = fresh_workspace;
$M = open_window;
cmd 'mark target';
$S = open_window;
cmd 'move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($ws);
is(@{$nodes}, 2, 'there are two containers');
is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
###############################################################################
# Given 'S' and 'M' in a horizontal split, when 'S' is moved to 'M', then
# both containers switch places.
###############################################################################
$ws = fresh_workspace;
$S = open_window;
$M = open_window;
cmd 'mark target';
cmd 'focus left';
cmd 'move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($ws);
is(@{$nodes}, 2, 'there are two containers');
is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
###############################################################################
# Given 'S' and no container 'M' exists, when 'S' is moved to 'M', then
# the command is unsuccessful.
###############################################################################
$ws = fresh_workspace;
$S = open_window;
$cmd_result = cmd 'move container to mark absent';
is($cmd_result->[0]->{success}, 0, 'command was unsuccessful');
###############################################################################
# Given 'S' and 'M' on different workspaces, when 'S' is moved to 'M', then
# 'S' ends up on the same workspace as 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$M = open_window;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($source_ws);
is(@{$nodes}, 0, 'source workspace is empty');
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 2, 'both containers are on the target workspace');
is($nodes->[0]->{window}, $M->{id}, 'M is left of S');
is($nodes->[1]->{window}, $S->{id}, 'S is right of M');
###############################################################################
# Given 'S' and 'M' on different workspaces and 'S' is urgent, when 'S' is
# moved to 'M', then the urgency flag is transferred to the target workspace.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$F = open_window;
$target_ws = fresh_workspace;
$M = open_window;
cmd 'mark target';
cmd 'workspace ' . $source_ws;
set_urgency($S, 1);
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
$source_ws = get_ws($source_ws);
$target_ws = get_ws($target_ws);
ok(!$source_ws->{urgent}, 'source workspace is no longer urgent');
ok($target_ws->{urgent}, 'target workspace is urgent');
###############################################################################
# Given 'S' and 'M' where 'M' is inside a tabbed container, when 'S' is moved
# to 'M', then 'S' ends up as a new tab.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
# open tabbed container ['A' 'M' 'B']
$target_ws = fresh_workspace;
$A = open_window;
cmd 'layout tabbed';
$M = open_window;
cmd 'mark target';
$B = open_window;
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'there is a tabbed container');
$nodes = $nodes->[0]->{nodes};
is(@{$nodes}, 4, 'all four containers are on the target workspace');
is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
is($nodes->[3]->{window}, $B->{id}, 'B is the fourth tab');
###############################################################################
# Given 'S' and 'M' where 'M' is a tabbed container where the currently focused
# tab is a nested layout, when 'S' is moved to 'M', then 'S' is a new tab
# within 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$A = open_window;
cmd 'layout tabbed';
cmd 'focus parent';
cmd 'mark target';
cmd 'focus child';
$B = open_window;
cmd 'split h';
$F = open_window;
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'there is a tabbed container');
$nodes = $nodes->[0]->{nodes};
is(@{$nodes}, 3, 'there are three tabs');
is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
###############################################################################
# Given 'S' and 'M' where 'M' is inside a split container inside a tabbed
# container, when 'S' is moved to 'M', then 'S' ends up as a container
# within the same tab as 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
# open tabbed container ['A'['B' 'M']]
$target_ws = fresh_workspace;
$A = open_window;
cmd 'layout tabbed';
$B = open_window;
cmd 'split h';
$M = open_window;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'there is a tabbed container');
$nodes = $nodes->[0]->{nodes};
is(@{$nodes}, 2, 'there are two tabs');
$nodes = $nodes->[1]->{nodes};
is(@{$nodes}, 3, 'the tab with the marked children has three children');
is($nodes->[0]->{window}, $B->{id}, 'B is the first tab');
is($nodes->[1]->{window}, $M->{id}, 'M is the second tab');
is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
###############################################################################
# Given 'S', 'A' and 'B' where 'A' and 'B' are inside the tabbed container 'M',
# when 'S' is moved to 'M', then 'S' ends up as a new tab in 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$A = open_window;
cmd 'layout tabbed';
$B = open_window;
cmd 'focus parent';
cmd 'mark target';
cmd 'focus child';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'there is a tabbed container');
$nodes = $nodes->[0]->{nodes};
is(@{$nodes}, 3, 'there are three tabs');
is($nodes->[0]->{window}, $A->{id}, 'A is the first tab');
is($nodes->[1]->{window}, $B->{id}, 'B is the second tab');
is($nodes->[2]->{window}, $S->{id}, 'S is the third tab');
###############################################################################
# Given 'S', 'A', 'F' and 'M', where 'M' is a workspace containing a tabbed
# container, when 'S' is moved to 'M', then 'S' does not end up as a tab, but
# rather as a new window next to the tabbed container.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$A = open_window;
cmd 'layout tabbed';
$F = open_window;
$M = $target_ws;
cmd 'focus parent';
cmd 'focus parent';
cmd 'mark target';
cmd 'focus ' . $source_ws;
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 2, 'there is a tabbed container and a window');
is($nodes->[1]->{window}, $S->{id}, 'S is the second window');
###############################################################################
# Given 'S' and 'M' where 'S' is floating and 'M' on a different workspace,
# when 'S' is moved to 'M', then 'S' is a floating container on the same
# workspaces as 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_floating_window;
$target_ws = fresh_workspace;
$M = open_window;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
is(@{get_ws($target_ws)->{floating_nodes}}, 1, 'target workspace has the container now');
###############################################################################
# Given 'S' and 'M' where 'M' is floating and on a different workspace,
# when 'S' is moved to 'M', then 'S' ends up as a tiling container on the
# same workspace as 'M'.
###############################################################################
$source_ws = fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$M = open_floating_window;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'tiling container moved to the target workspace');
###############################################################################
# Given 'S' and 'M' where 'M' is inside a floating container but not its direct
# child, when 'S' is moved to 'M', i3 should not crash.
# See issue: #3402
###############################################################################
$target_ws = fresh_workspace;
$S = open_window;
open_window;
cmd 'splitv';
$M = open_window;
cmd 'mark target';
cmd 'focus parent, floating enable, focus child';
cmd '[id="' . $S->{id} . '"] move container to mark target';
does_i3_live;
# Note: this is not actively supported behavior.
$nodes = get_ws($target_ws)->{floating_nodes}->[0]->{nodes}->[0]->{nodes};
is(1, (grep { $_->{window} == $S->{id} } @{$nodes}), 'tiling container moved inside floating container');
###############################################################################
# Given 'S' and 'M' are the same container, when 'S' is moved to 'M', then
# the command is ignored.
###############################################################################
$ws = fresh_workspace;
$S = open_window;
$M = $S;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
does_i3_live;
###############################################################################
# Given 'S' and 'M' where 'M' is a workspace and 'S' is on a different
# workspace, then 'S' ends up as a tiling container on 'M'.
# See issue: #2003
###############################################################################
fresh_workspace;
$S = open_window;
$target_ws = fresh_workspace;
$M = $target_ws;
cmd 'mark target';
cmd '[id="' . $S->{id} . '"] move container to mark target';
sync_with_i3;
does_i3_live;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 1, 'tiling container moved to the target workspace');
###############################################################################
# Given 'S' and 'M' where 'S' is a workspace and 'M' is a container on a
# different workspace, then all the contents of workspace 'S' end up in 'M's
# workspace.
###############################################################################
$S = fresh_workspace;
cmd 'mark S';
open_window;
open_window;
cmd 'splitv';
open_window;
open_floating_window;
$target_ws = fresh_workspace;
$M = open_window;
cmd 'mark target';
cmd '[con_mark=S] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($target_ws);
is(@{$nodes}, 2, 'there is a window and a container with the contents of the original workspace');
is($nodes->[0]->{window}, $M->{id}, 'M remains the first window');
is(@{get_ws($target_ws)->{floating_nodes}}, 1, 'target workspace has the floating container');
###############################################################################
# Given 'S' and 'M', where 'S' is a container and 'M' is a container hidden in
# the scratchpad, then move 'S' to the scratchpad
###############################################################################
$ws = fresh_workspace;
$S = open_window;
cmd 'mark S';
$M = open_window;
cmd 'mark target';
cmd 'move container to scratchpad';
cmd '[con_mark=S] move container to mark target';
sync_with_i3;
($nodes, $focus) = get_ws_content($ws);
is(@{$nodes}, 0, 'there are no tiling windows on the workspace');
is(@{get_ws($ws)->{floating_nodes}}, 0, 'there are no floating containers on the workspace');
$__i3_scratch = get_ws('__i3_scratch');
is(@{$__i3_scratch->{nodes}}, 0, 'there are no tiling windows on the scratchpad workspace');
is(@{$__i3_scratch->{floating_nodes}}, 2, 'there are two floating containers in the scratchpad');
###############################################################################
done_testing;