Implement the tick event

This makes our tests less flaky, shorter, and more readable.

fixes #2988
This commit is contained in:
Michael Stapelberg
2017-09-24 15:40:30 +02:00
parent 14c8cf8622
commit ce21de8dde
29 changed files with 457 additions and 734 deletions

View File

@ -47,6 +47,8 @@ our @EXPORT = qw(
wait_for_unmap
$x
kill_all_windows
events_for
listen_for_binding
);
=head1 NAME
@ -900,6 +902,86 @@ sub kill_all_windows {
cmd '[title=".*"] kill';
}
=head2 events_for($subscribecb, [ $rettype ], [ $eventcbs ])
Helper function which returns an array containing all events of type $rettype
which were generated by i3 while $subscribecb was running.
Set $eventcbs to subscribe to multiple event types and/or perform your own event
aggregation.
=cut
sub events_for {
my ($subscribecb, $rettype, $eventcbs) = @_;
my @events;
$eventcbs //= {};
if (defined($rettype)) {
$eventcbs->{$rettype} = sub { push @events, shift };
}
my $subscribed = AnyEvent->condvar;
my $flushed = AnyEvent->condvar;
$eventcbs->{tick} = sub {
my ($event) = @_;
if ($event->{first}) {
$subscribed->send($event);
} else {
$flushed->send($event);
}
};
my $i3 = i3(get_socket_path(0));
$i3->connect->recv;
$i3->subscribe($eventcbs)->recv;
$subscribed->recv;
# Subscription established, run the callback.
$subscribecb->();
# Now generate a tick event, which we know well receive (and at which point
# all other events have been received).
my $nonce = int(rand(255)) + 1;
$i3->send_tick($nonce);
my $tick = $flushed->recv;
$tester->is_eq($tick->{payload}, $nonce, 'tick nonce received');
return @events;
}
=head2 listen_for_binding($cb)
Helper function to evaluate whether sending KeyPress/KeyRelease events via XTEST
triggers an i3 key binding or not. Expects key bindings to be configured in the
form “bindsym <binding> nop <binding>”, e.g. “bindsym Mod4+Return nop
Mod4+Return”.
is(listen_for_binding(
sub {
xtest_key_press(133); # Super_L
xtest_key_press(36); # Return
xtest_key_release(36); # Return
xtest_key_release(133); # Super_L
xtest_sync_with_i3;
},
),
'Mod4+Return',
'triggered the "Mod4+Return" keybinding');
=cut
sub listen_for_binding {
my ($cb) = @_;
my $triggered = AnyEvent->condvar;
my @events = events_for(
$cb,
'binding');
$tester->is_eq(scalar @events, 1, 'Received precisely one event');
$tester->is_eq($events[0]->{change}, 'run', 'change is "run"');
# We look at the command (which is “nop <binding>”) because that is easier
# than re-assembling the string representation of $event->{binding}.
my $command = $events[0]->{binding}->{command};
$command =~ s/^nop //g;
return $command;
}
=head1 AUTHOR
Michael Stapelberg <michael@i3wm.org>

View File

@ -20,8 +20,6 @@ our @EXPORT = qw(
xtest_key_release
xtest_button_press
xtest_button_release
listen_for_binding
start_binding_capture
binding_events
);
@ -256,86 +254,6 @@ sub import {
=cut
my $i3;
our @binding_events;
=head2 start_binding_capture()
Captures all binding events sent by i3 in the C<@binding_events> symbol, so
that you can verify the correct number of binding events was generated.
my $pid = launch_with_config($config);
start_binding_capture;
# …
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 2, 'Received exactly 2 binding events');
=cut
sub start_binding_capture {
# Store a copy of each binding event so that we can count the expected
# events in test cases.
$i3 = i3(get_socket_path());
$i3->connect()->recv;
$i3->subscribe({
binding => sub {
my ($event) = @_;
@binding_events = (@binding_events, $event);
},
})->recv;
}
=head2 listen_for_binding($cb)
Helper function to evaluate whether sending KeyPress/KeyRelease events via
XTEST triggers an i3 key binding or not (with a timeout of 0.5s). Expects key
bindings to be configured in the form “bindsym <binding> nop <binding>”, e.g.
“bindsym Mod4+Return nop Mod4+Return”.
is(listen_for_binding(
sub {
xtest_key_press(133); # Super_L
xtest_key_press(36); # Return
xtest_key_release(36); # Return
xtest_key_release(133); # Super_L
},
),
'Mod4+Return',
'triggered the "Mod4+Return" keybinding');
=cut
sub listen_for_binding {
my ($cb) = @_;
my $triggered = AnyEvent->condvar;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
$i3->subscribe({
binding => sub {
my ($event) = @_;
return unless $event->{change} eq 'run';
# We look at the command (which is “nop <binding>”) because that is
# easier than re-assembling the string representation of
# $event->{binding}.
$triggered->send($event->{binding}->{command});
},
})->recv;
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$triggered->send('timeout');
}
);
$cb->();
my $recv = $triggered->recv;
$recv =~ s/^nop //g;
return $recv;
}
=head2 set_xkb_group($group)
Changes the current XKB group from the default of 1 to C<$group>, which must be

View File

@ -16,61 +16,25 @@
use i3test;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
################################
# Workspaces requests and events
################################
my $old_ws = get_ws(focused_ws());
# Events
# We are switching to an empty workpspace from an empty workspace, so we expect
# to receive "init", "focus", and "empty".
my $init = AnyEvent->condvar;
my $focus = AnyEvent->condvar;
my $empty = AnyEvent->condvar;
$i3->subscribe({
workspace => sub {
my ($event) = @_;
if ($event->{change} eq 'init') {
$init->send($event);
} elsif ($event->{change} eq 'focus') {
$focus->send($event);
} elsif ($event->{change} eq 'empty') {
$empty->send($event);
}
}
})->recv;
cmd 'workspace 2';
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$init->send(0);
$focus->send(0);
$empty->send(0);
}
);
my $init_event = $init->recv;
my $focus_event = $focus->recv;
my $empty_event = $empty->recv;
my @events = events_for(
sub { cmd 'workspace 2' },
'workspace');
my $current_ws = get_ws(focused_ws());
ok($init_event, 'workspace "init" event received');
is($init_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the initted workspace con');
is(scalar @events, 3, 'Received 3 events');
is($events[0]->{change}, 'init', 'First event has change = init');
is($events[0]->{current}->{id}, $current_ws->{id}, 'the "current" property contains the initted workspace con');
ok($focus_event, 'workspace "focus" event received');
is($focus_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con');
is($focus_event->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last');
is($events[1]->{change}, 'focus', 'Second event has change = focus');
is($events[1]->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con');
is($events[1]->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last');
ok($empty_event, 'workspace "empty" event received');
is($empty_event->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con');
is($events[2]->{change}, 'empty', 'Third event has change = empty');
is($events[2]->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con');
done_testing;

View File

@ -28,24 +28,11 @@ mode "with spaces" {
}
EOT
my $i3 = i3(get_socket_path(0));
$i3->connect->recv;
my @events = events_for(
sub { cmd 'mode "m1"' },
'mode');
my $cv = AnyEvent->condvar;
$i3->subscribe({
mode => sub {
my ($event) = @_;
$cv->send($event->{change} eq 'm1');
}
})->recv;
cmd 'mode "m1"';
# Timeout after 0.5s
my $t;
$t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0); });
ok($cv->recv, 'Mode event received');
my @changes = map { $_->{change} } @events;
is_deeply(\@changes, [ 'm1' ], 'Mode event received');
done_testing;

View File

@ -16,46 +16,15 @@
use i3test;
SKIP: {
skip "AnyEvent::I3 too old (need >= 0.15)", 1 if $AnyEvent::I3::VERSION < 0.15;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
################################
# Window event
################################
# Events
my $new = AnyEvent->condvar;
my $focus = AnyEvent->condvar;
$i3->subscribe({
window => sub {
my ($event) = @_;
if ($event->{change} eq 'new') {
$new->send($event);
} elsif ($event->{change} eq 'focus') {
$focus->send($event);
}
}
})->recv;
open_window;
my @events = events_for(
sub { open_window },
'window');
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$new->send(0);
$focus->send(0);
}
);
is($new->recv->{container}->{focused}, 0, 'Window "new" event received');
is($focus->recv->{container}->{focused}, 1, 'Window "focus" event received');
}
is(scalar @events, 2, 'Received 2 events');
is($events[0]->{container}->{focused}, 0, 'Window "new" event received');
is($events[1]->{container}->{focused}, 1, 'Window "focus" event received');
done_testing;

View File

@ -16,13 +16,6 @@
use i3test;
SKIP: {
skip "AnyEvent::I3 too old (need >= 0.15)", 1 if $AnyEvent::I3::VERSION < 0.15;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
################################
# Window focus event
################################
@ -33,62 +26,29 @@ my $win0 = open_window;
my $win1 = open_window;
my $win2 = open_window;
my $focus = AnyEvent->condvar;
$i3->subscribe({
window => sub {
my ($event) = @_;
$focus->send($event);
}
})->recv;
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$focus->send(0);
}
);
# ensure the rightmost window contains input focus
$i3->command('[id="' . $win2->id . '"] focus')->recv;
cmd '[id="' . $win2->id . '"] focus';
is($x->input_focus, $win2->id, "Window 2 focused");
cmd 'focus left';
my $event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($focus->recv->{container}->{name}, 'Window 1', 'Window 1 focused');
sub focus_subtest {
my ($cmd, $name) = @_;
$focus = AnyEvent->condvar;
cmd 'focus left';
$event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($event->{container}->{name}, 'Window 0', 'Window 0 focused');
my $focus = AnyEvent->condvar;
$focus = AnyEvent->condvar;
cmd 'focus right';
$event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($event->{container}->{name}, 'Window 1', 'Window 1 focused');
$focus = AnyEvent->condvar;
cmd 'focus right';
$event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($event->{container}->{name}, 'Window 2', 'Window 2 focused');
$focus = AnyEvent->condvar;
cmd 'focus right';
$event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($event->{container}->{name}, 'Window 0', 'Window 0 focused');
$focus = AnyEvent->condvar;
cmd 'focus left';
$event = $focus->recv;
is($event->{change}, 'focus', 'Focus event received');
is($event->{container}->{name}, 'Window 2', 'Window 2 focused');
my @events = events_for(
sub { cmd $cmd },
'window');
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{change}, 'focus', 'Focus event received');
is($events[0]->{container}->{name}, $name, "$name focused");
}
subtest 'focus left (1)', \&focus_subtest, 'focus left', 'Window 1';
subtest 'focus left (2)', \&focus_subtest, 'focus left', 'Window 0';
subtest 'focus right (1)', \&focus_subtest, 'focus right', 'Window 1';
subtest 'focus right (2)', \&focus_subtest, 'focus right', 'Window 2';
subtest 'focus right (3)', \&focus_subtest, 'focus right', 'Window 0';
subtest 'focus left', \&focus_subtest, 'focus left', 'Window 2';
done_testing;

View File

@ -16,42 +16,17 @@
use i3test;
SKIP: {
skip "AnyEvent::I3 too old (need >= 0.15)", 1 if $AnyEvent::I3::VERSION < 0.15;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
################################
# Window title event
################################
my $window = open_window(name => 'Window 0');
my $title = AnyEvent->condvar;
my @events = events_for(
sub {
$window->name('New Window Title');
sync_with_i3;
},
'window');
$i3->subscribe({
window => sub {
my ($event) = @_;
$title->send($event);
}
})->recv;
$window->name('New Window Title');
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$title->send(0);
}
);
my $event = $title->recv;
is($event->{change}, 'title', 'Window title change event received');
is($event->{container}->{name}, 'New Window Title', 'Window title changed');
}
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{change}, 'title', 'Window title change event received');
is($events[0]->{container}->{name}, 'New Window Title', 'Window title changed');
done_testing;

View File

@ -19,41 +19,19 @@
# Bug still in: 4.7.2-135-g7deb23c
use i3test;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
open_window;
my $cv;
my $t;
sub fullscreen_subtest {
my ($want) = @_;
my @events = events_for(
sub { cmd 'fullscreen' },
'window');
sub reset_test {
$cv = AE::cv;
$t = AE::timer(0.5, 0, sub { $cv->send(0); });
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{container}->{fullscreen_mode}, $want, "fullscreen_mode now $want");
}
reset_test;
$i3->subscribe({
window => sub {
my ($e) = @_;
if ($e->{change} eq 'fullscreen_mode') {
$cv->send($e->{container});
}
},
})->recv;
my $window = open_window;
cmd 'fullscreen';
my $con = $cv->recv;
ok($con, 'got fullscreen window event (on)');
is($con->{fullscreen_mode}, 1, 'window is fullscreen');
reset_test;
cmd 'fullscreen';
$con = $cv->recv;
ok($con, 'got fullscreen window event (off)');
is($con->{fullscreen_mode}, 0, 'window is not fullscreen');
subtest 'fullscreen on', \&fullscreen_subtest, 1;
subtest 'fullscreen off', \&fullscreen_subtest, 0;
done_testing;

View File

@ -18,12 +18,8 @@
#
use i3test;
SKIP: {
skip "AnyEvent::I3 too old (need >= 0.15)", 1 if $AnyEvent::I3::VERSION < 0.15;
################################################################################
# check that the workspace empty event is send upon workspace switch when the
# check that the workspace empty event is sent upon workspace switch when the
# old workspace is empty
################################################################################
subtest 'Workspace empty event upon switch', sub {
@ -35,26 +31,17 @@ subtest 'Workspace empty event upon switch', sub {
cmd '[id="' . $w1->id . '"] kill';
my $cond = AnyEvent->condvar;
my $client = i3(get_socket_path(0));
$client->connect()->recv;
$client->subscribe({
workspace => sub {
my ($event) = @_;
$cond->send($event);
}
})->recv;
my @events = events_for(
sub { cmd "workspace $ws2" },
'workspace');
cmd "workspace $ws2";
sync_with_i3;
my $event = $cond->recv;
is($event->{change}, 'empty', '"Empty" event received upon workspace switch');
is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
is(scalar @events, 2, 'Received 2 event');
is($events[1]->{change}, 'empty', '"Empty" event received upon workspace switch');
is($events[1]->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
};
################################################################################
# check that no workspace empty event is send upon workspace switch if the
# check that no workspace empty event is sent upon workspace switch if the
# workspace is not empty
################################################################################
subtest 'No workspace empty event', sub {
@ -63,36 +50,16 @@ subtest 'No workspace empty event', sub {
my $ws1 = fresh_workspace;
my $w1 = open_window();
my @events;
my $cond = AnyEvent->condvar;
my $client = i3(get_socket_path(0));
$client->connect()->recv;
$client->subscribe({
workspace => sub {
my ($event) = @_;
push @events, $event;
}
})->recv;
my @events = events_for(
sub { cmd "workspace $ws2" },
'workspace');
# Wait for the workspace event on a new connection. Events will be delivered
# to older connections earlier, so by the time it arrives here, it should be
# in @events already.
my $ws_event_block_conn = i3(get_socket_path(0));
$ws_event_block_conn->connect()->recv;
$ws_event_block_conn->subscribe({ workspace => sub { $cond->send(1) }});
cmd "workspace $ws2";
sync_with_i3;
my @expected_events = grep { $_->{change} eq 'focus' } @events;
my @empty_events = grep { $_->{change} eq 'empty' } @events;
is(@expected_events, 1, '"Focus" event received');
is(@empty_events, 0, 'No "empty" events received');
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{change}, 'focus', 'Event change is "focus"');
};
################################################################################
# check that workspace empty event is send when the last window has been closed
# check that workspace empty event is sent when the last window has been closed
# on invisible workspace
################################################################################
subtest 'Workspace empty event upon window close', sub {
@ -101,25 +68,16 @@ subtest 'Workspace empty event upon window close', sub {
my $ws2 = fresh_workspace;
my $w2 = open_window();
my $cond = AnyEvent->condvar;
my $client = i3(get_socket_path(0));
$client->connect()->recv;
$client->subscribe({
workspace => sub {
my ($event) = @_;
$cond->send($event);
}
})->recv;
my @events = events_for(
sub {
$w1->unmap;
sync_with_i3;
},
'workspace');
cmd '[id="' . $w1->id . '"] kill';
sync_with_i3;
my $event = $cond->recv;
is($event->{change}, 'empty', '"Empty" event received upon window close');
is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{change}, 'empty', '"Empty" event received upon window close');
is($events[0]->{current}->{name}, $ws1, '"current" property should be set to the workspace con');
};
}
done_testing;

View File

@ -19,41 +19,22 @@
# Bug still in: 4.8-7-gf4a8253
use i3test;
my $i3 = i3(get_socket_path());
$i3->connect->recv;
sub floating_subtest {
my ($win, $cmd, $want) = @_;
my $cv = AnyEvent->condvar;
my @events = events_for(
sub { cmd $cmd },
'window');
$i3->subscribe({
window => sub {
my ($event) = @_;
$cv->send($event) if $event->{change} eq 'floating';
}
})->recv;
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$cv->send(0);
}
);
my @floating = grep { $_->{change} eq 'floating' } @events;
is(scalar @floating, 1, 'Received 1 floating event');
is($floating[0]->{container}->{window}, $win->{id}, "window id matches");
is($floating[0]->{container}->{floating}, $want, "floating is $want");
}
my $win = open_window();
cmd '[id="' . $win->{id} . '"] floating enable';
my $e = $cv->recv;
isnt($e, 0, 'floating a container should send an ipc window event');
is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window');
is($e->{container}->{floating}, 'user_on', 'the container should be floating');
$cv = AnyEvent->condvar;
cmd '[id="' . $win->{id} . '"] floating disable';
$e = $cv->recv;
isnt($e, 0, 'disabling floating on a container should send an ipc window event');
is($e->{container}->{window}, $win->{id}, 'the event should contain information about the window');
is($e->{container}->{floating}, 'user_off', 'the container should not be floating');
subtest 'floating enable', \&floating_subtest, $win, '[id="' . $win->{id} . '"] floating enable', 'user_on';
subtest 'floating disable', \&floating_subtest, $win, '[id="' . $win->{id} . '"] floating disable', 'user_off';
done_testing;

View File

@ -35,51 +35,38 @@ SKIP: {
skip 'xdotool is required to test the binding event. `[apt-get install|pacman -S] xdotool`', 1 if $?;
skip "AnyEvent::I3 too old (need >= 0.16)", 1 if $AnyEvent::I3::VERSION < 0.16;
my $pid = launch_with_config($config);
my $i3 = i3(get_socket_path());
$i3->connect->recv;
my $cv = AnyEvent->condvar;
my $cv = AE::cv;
my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
my @events = events_for(
sub {
# TODO: this is still flaky: we need to synchronize every X11
# connection with i3. Move to XTEST and synchronize that connection.
qx(xdotool key $binding_symbol);
},
'binding');
$i3->subscribe({
binding => sub {
$cv->send(shift);
}
})->recv;
is(scalar @events, 1, 'Received 1 event');
qx(xdotool key $binding_symbol);
my $e = $cv->recv;
does_i3_live;
diag "Event:\n", Dumper($e);
ok($e,
'the binding event should emit when user input triggers an i3 binding event');
is($e->{change}, 'run',
is($events[0]->{change}, 'run',
'the `change` field should indicate this binding has run');
ok($e->{binding},
ok($events[0]->{binding},
'the `binding` field should be a hash that contains information about the binding');
is($e->{binding}->{input_type}, 'keyboard',
is($events[0]->{binding}->{input_type}, 'keyboard',
'the input_type field should be the input type of the binding (keyboard or mouse)');
note 'the `mods` field should contain the symbols for the modifiers of the binding';
foreach (@mods) {
ok(grep(/$_/i, @{$e->{binding}->{mods}}), "`mods` contains the modifier $_");
ok(grep(/$_/i, @{$events[0]->{binding}->{mods}}), "`mods` contains the modifier $_");
}
is($e->{binding}->{command}, $command,
is($events[0]->{binding}->{command}, $command,
'the `command` field should contain the command the binding ran');
is($e->{binding}->{input_code}, 0,
is($events[0]->{binding}->{input_code}, 0,
'the input_code should be the specified code if the key was bound with bindcode, and otherwise zero');
exit_gracefully($pid);

View File

@ -36,8 +36,6 @@ SKIP: {
skip "setxkbmap not found", 1 if
system(q|setxkbmap -print >/dev/null|) != 0;
start_binding_capture;
system(q|setxkbmap us,ru -option grp:alt_shift_toggle|);
is(listen_for_binding(
@ -87,9 +85,6 @@ is(listen_for_binding(
'Mod4+Return',
'triggered the "Mod4+Return" keybinding');
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events');
# Disable the grp:alt_shift_toggle option, as we use Alt+Shift in other testcases.
system(q|setxkbmap us -option|);

View File

@ -37,8 +37,6 @@ SKIP: {
skip "libxcb-xkb too old (need >= 1.11)", 1 unless
ExtUtils::PkgConfig->atleast_version('xcb-xkb', '1.11');
start_binding_capture;
is(listen_for_binding(
sub {
xtest_key_press(107); # Print
@ -87,9 +85,6 @@ is(listen_for_binding(
'Mod1+Shift+b release',
'triggered the "Mod1+Shift+b" release keybinding');
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 4, 'Received exactly 4 binding events');
}
done_testing;

View File

@ -28,23 +28,11 @@ EOT
cmd 'mode othermode';
my $i3 = i3(get_socket_path(0));
$i3->connect->recv;
my @events = events_for(
sub { cmd 'reload' },
'mode');
my $cv = AnyEvent->condvar;
$i3->subscribe({
mode => sub {
my ($event) = @_;
$cv->send($event->{change} eq 'default');
}
})->recv;
cmd 'reload';
# Timeout after 0.5s
my $t;
$t = AnyEvent->timer(after => 0.5, cb => sub { $cv->send(0); });
ok($cv->recv, 'Mode event received');
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{change}, 'default', 'change is "default"');
done_testing;

View File

@ -18,27 +18,16 @@
# Ticket: #2501
use i3test;
my ($i3, $timer, $event, $mark);
sub mark_subtest {
my ($cmd) = @_;
$i3 = i3(get_socket_path());
$i3->connect()->recv;
my @events = events_for(
sub { cmd $cmd },
'window');
$i3->subscribe({
window => sub {
my ($event) = @_;
return unless defined $mark;
return unless $event->{change} eq 'mark';
$mark->send($event);
}
})->recv;
$timer = AnyEvent->timer(
after => 0.5,
cb => sub {
$mark->send(0);
}
);
my @mark = grep { $_->{change} eq 'mark' } @events;
is(scalar @mark, 1, 'Received 1 window::mark event');
}
###############################################################################
# Marking a container triggers a 'mark' event.
@ -46,11 +35,7 @@ $timer = AnyEvent->timer(
fresh_workspace;
open_window;
$mark = AnyEvent->condvar;
cmd 'mark x';
$event = $mark->recv;
ok($event, 'window::mark event has been received');
subtest 'mark', \&mark_subtest, 'mark x';
###############################################################################
# Unmarking a container triggers a 'mark' event.
@ -59,11 +44,7 @@ fresh_workspace;
open_window;
cmd 'mark x';
$mark = AnyEvent->condvar;
cmd 'unmark x';
$event = $mark->recv;
ok($event, 'window::mark event has been received');
subtest 'unmark', \&mark_subtest, 'unmark x';
###############################################################################

View File

@ -19,34 +19,17 @@
# Bug still in: 4.8-7-gf4a8253
use i3test;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
my $cv;
my $t;
sub reset_test {
$cv = AE::cv;
$t = AE::timer(0.5, 0, sub { $cv->send(0); });
}
reset_test;
$i3->subscribe({
window => sub {
my ($e) = @_;
if ($e->{change} eq 'close') {
$cv->send($e->{container});
}
},
})->recv;
my $window = open_window;
cmd 'kill';
my $con = $cv->recv;
my @events = events_for(
sub {
$window->unmap;
sync_with_i3;
},
'window');
ok($con, 'closing a window should send the window::close event');
is($con->{window}, $window->{id}, 'the event should contain information about the window');
my @close = grep { $_->{change} eq 'close' } @events;
is(scalar @close, 1, 'Received 1 window::close event');
is($close[0]->{container}->{window}, $window->{id}, 'the event should contain information about the window');
done_testing;

View File

@ -19,43 +19,22 @@
# Bug still in: 4.8-7-gf4a8253
use i3test;
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
my $cv;
my $t;
sub reset_test {
$cv = AE::cv;
$t = AE::timer(0.5, 0, sub { $cv->send(0); });
}
reset_test;
$i3->subscribe({
window => sub {
my ($e) = @_;
if ($e->{change} eq 'move') {
$cv->send($e->{container});
}
},
})->recv;
my $dummy_window = open_window;
my $window = open_window;
cmd 'move right';
my $con = $cv->recv;
sub move_subtest {
my ($cmd) = @_;
my $cv = AnyEvent->condvar;
my @events = events_for(
sub { cmd $cmd },
'window');
ok($con, 'moving a window should emit the window::move event');
is($con->{window}, $window->{id}, 'the event should contain info about the window');
my @move = grep { $_->{change} eq 'move' } @events;
is(scalar @move, 1, 'Received 1 window::move event');
is($move[0]->{container}->{window}, $window->{id}, 'window id matches');
}
reset_test;
cmd 'move to workspace ws_new';
$con = $cv->recv;
ok($con, 'moving a window to a different workspace should emit the window::move event');
is($con->{window}, $window->{id}, 'the event should contain info about the window');
subtest 'move right', \&move_subtest, 'move right';
subtest 'move to workspace', \&move_subtest, 'move to workspace ws_new';
done_testing;

View File

@ -19,50 +19,37 @@
#
use i3test;
my $config = <<EOT;
# i3 config file (v4)
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
force_display_urgency_hint 0ms
EOT
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
my $cv;
$i3->subscribe({
window => sub {
my ($event) = @_;
$cv->send($event) if $event->{change} eq 'urgent';
}
})->recv;
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$cv->send(0);
}
);
$cv = AnyEvent->condvar;
fresh_workspace;
my $win = open_window;
my $dummy_win = open_window;
$win->add_hint('urgency');
my $event = $cv->recv;
sub urgency_subtest {
my ($subscribecb, $win, $want) = @_;
isnt($event, 0, 'an urgent con should emit the window::urgent event');
is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
is($event->{container}->{urgent}, 1, 'the container should be urgent');
my @events = events_for(
$subscribecb,
'window');
$cv = AnyEvent->condvar;
$win->delete_hint('urgency');
$event = $cv->recv;
my @urgent = grep { $_->{change} eq 'urgent' } @events;
is(scalar @urgent, 1, 'Received 1 window::urgent event');
is($urgent[0]->{container}->{window}, $win->{id}, "window id matches");
is($urgent[0]->{container}->{urgent}, $want, "urgent is $want");
}
isnt($event, 0, 'an urgent con should emit the window::urgent event');
is($event->{container}->{window}, $win->{id}, 'the event should contain information about the window');
is($event->{container}->{urgent}, 0, 'the container should not be urgent');
subtest "urgency set", \&urgency_subtest,
sub {
$win->add_hint('urgency');
sync_with_i3;
},
$win,
1;
subtest "urgency unset", \&urgency_subtest,
sub {
$win->delete_hint('urgency');
sync_with_i3;
},
$win,
0;
done_testing;

View File

@ -23,13 +23,13 @@
# Bug still in: 4.12-46-g2123888
use i3test;
SKIP: {
skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
# We cannot use events_for in this test as we cannot send events after
# issuing the restart/shutdown command.
my $i3 = i3(get_socket_path());
$i3->connect->recv;
my $cv = AE::cv;
my $cv = AnyEvent->condvar;
my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
$i3->subscribe({
@ -50,7 +50,7 @@ is($e->{change}, 'restart', 'the `change` field should tell the reason for the s
$i3 = i3(get_socket_path());
$i3->connect->recv;
$cv = AE::cv;
$cv = AnyEvent->condvar;
$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
$i3->subscribe({
@ -66,6 +66,5 @@ $e = $cv->recv;
diag "Event:\n", Dumper($e);
ok($e, 'the shutdown event should emit when the ipc is exited by command');
is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
}
done_testing;

View File

@ -51,8 +51,6 @@ EOT
my $pid = launch_with_config($config);
start_binding_capture;
is(listen_for_binding(
sub {
xtest_key_press(87); # KP_End
@ -213,9 +211,6 @@ is(listen_for_binding(
's',
'triggered the "s" keybinding with Num_Lock');
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 12, 'Received exactly 12 binding events');
exit_gracefully($pid);
################################################################################
@ -234,8 +229,6 @@ EOT
$pid = launch_with_config($config);
start_binding_capture;
is(listen_for_binding(
sub {
xtest_key_press(133); # Super_L
@ -288,9 +281,6 @@ is(listen_for_binding(
'Return',
'triggered the "Return" keybinding with Num_Lock');
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 16, 'Received exactly 16 binding events');
exit_gracefully($pid);
################################################################################
@ -307,8 +297,6 @@ EOT
$pid = launch_with_config($config);
start_binding_capture;
is(listen_for_binding(
sub {
xtest_key_press(87); # KP_End
@ -329,7 +317,7 @@ is(listen_for_binding(
'KP_Down',
'triggered the "KP_Down" keybinding');
is(listen_for_binding(
my @unexpected = events_for(
sub {
xtest_key_press(77); # enable Num_Lock
xtest_key_release(77); # enable Num_Lock
@ -339,11 +327,10 @@ is(listen_for_binding(
xtest_key_release(77); # disable Num_Lock
xtest_sync_with_i3;
},
),
'timeout',
'Did not trigger the KP_End keybinding with KP_1');
'binding');
is(scalar @unexpected, 0, 'Did not trigger the KP_End keybinding with KP_1');
is(listen_for_binding(
my @unexpected2 = events_for(
sub {
xtest_key_press(77); # enable Num_Lock
xtest_key_release(77); # enable Num_Lock
@ -353,15 +340,12 @@ is(listen_for_binding(
xtest_key_release(77); # disable Num_Lock
xtest_sync_with_i3;
},
),
'timeout',
'Did not trigger the KP_Down keybinding with KP_2');
'binding');
is(scalar @unexpected2, 0, 'Did not trigger the KP_Down keybinding with KP_2');
# TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
sync_with_i3;
is(scalar @i3test::XTEST::binding_events, 18, 'Received exactly 18 binding events');
exit_gracefully($pid);
################################################################################
@ -379,8 +363,6 @@ $pid = launch_with_config($config);
my $win = open_window;
start_binding_capture;
is(listen_for_binding(
sub {
xtest_key_press(77); # enable Num_Lock

View File

@ -13,7 +13,7 @@
#
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
# (unless you are already familiar with Perl)
#
#
# Ticket: #990
# Bug still in: 4.5.1-23-g82b5978
@ -23,46 +23,17 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
fake-outputs 1024x768+0+0,1024x768+1024+0
EOT
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
################################
# Workspaces requests and events
################################
my $old_ws = get_ws(focused_ws);
# Events
# We are switching to an empty workpspace on the output to the right from an empty workspace on the output on the left, so we expect
# to receive "init", "focus", and "empty".
my $focus = AnyEvent->condvar;
$i3->subscribe({
workspace => sub {
my ($event) = @_;
if ($event->{change} eq 'focus') {
$focus->send($event);
}
}
})->recv;
my $t;
$t = AnyEvent->timer(
after => 0.5,
cb => sub {
$focus->send(0);
}
);
cmd 'focus output right';
my $event = $focus->recv;
my @events = events_for(
sub { cmd 'focus output right' },
'workspace');
my $current_ws = get_ws(focused_ws);
ok($event, 'Workspace "focus" event received');
is($event->{current}->{id}, $current_ws->{id}, 'Event gave correct current workspace');
is($event->{old}->{id}, $old_ws->{id}, 'Event gave correct old workspace');
is(scalar @events, 1, 'Received 1 event');
is($events[0]->{current}->{id}, $current_ws->{id}, 'Event gave correct current workspace');
is($events[0]->{old}->{id}, $old_ws->{id}, 'Event gave correct old workspace');
done_testing;

View File

@ -27,51 +27,29 @@ workspace ws-left output fake-0
workspace ws-right output fake-1
EOT
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
# subscribe to the 'focus' ipc event
my $focus = AnyEvent->condvar;
$i3->subscribe({
workspace => sub {
my ($event) = @_;
if ($event->{change} eq 'focus') {
$focus->send($event);
}
}
})->recv;
# give up after 0.5 seconds
my $timer = AnyEvent->timer(
after => 0.5,
cb => sub {
$focus->send(0);
}
);
# open two windows on the left output
cmd 'workspace ws-left';
open_window;
open_window;
sub focus_subtest {
my ($cmd, $want) = @_;
my @events = events_for(
sub { cmd $cmd },
'workspace');
my @focus = grep { $_->{change} eq 'focus' } @events;
is(scalar @focus, 1, 'Received 1 workspace::focus event');
is($focus[0]->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
is(@{$focus[0]->{current}->{nodes}}, $want, 'focus event gave the right number of windows on the workspace');
}
# move a window over to the right output
cmd 'move right';
my $event = $focus->recv;
subtest 'move right (1)', \&focus_subtest, 'move right', 1;
ok($event, 'moving from workspace with two windows triggered focus ipc event');
is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
is(@{$event->{current}->{nodes}}, 1, 'focus event gave the right number of windows on the workspace');
# reset and try again
$focus = AnyEvent->condvar;
# move another window
cmd 'workspace ws-left';
$focus->recv;
$focus = AnyEvent->condvar;
cmd 'move right';
$event = $focus->recv;
ok($event, 'moving from workspace with one window triggered focus ipc event');
is($event->{current}->{name}, 'ws-right', 'focus event gave the right workspace');
is(@{$event->{current}->{nodes}}, 2, 'focus event gave the right number of windows on the workspace');
subtest 'move right (2)', \&focus_subtest, 'move right', 2;
done_testing;

View File

@ -34,17 +34,12 @@ bar {
EOT
use i3test::XTEST;
my ($cv, $timer);
sub reset_test {
$cv = AE::cv;
$timer = AE::timer(1, 0, sub { $cv->send(0); });
}
my $i3 = i3(get_socket_path());
$i3->connect()->recv;
my $ws = fresh_workspace;
reset_test;
my $cv = AnyEvent->condvar;
my $timer = AnyEvent->timer(1, 0, sub { $cv->send(0) });
$i3->subscribe({
window => sub {
my ($event) = @_;
@ -60,8 +55,6 @@ $i3->subscribe({
},
})->recv;
my $con;
sub i3bar_present {
my ($nodes) = @_;
@ -83,50 +76,68 @@ sub i3bar_present {
if (i3bar_present($i3->get_tree->recv->{nodes})) {
ok(1, 'i3bar present');
} else {
$con = $cv->recv;
my $con = $cv->recv;
ok($con, 'i3bar appeared');
}
my $left = open_window;
my $right = open_window;
sync_with_i3;
$con = $cv->recv;
my $con = $cv->recv;
is($con->{window}, $right->{id}, 'focus is initially on the right container');
reset_test;
xtest_button_press(1, 3, 3);
xtest_button_release(1, 3, 3);
sync_with_i3;
$con = $cv->recv;
is($con->{window}, $left->{id}, 'button 1 moves focus left');
reset_test;
sub focus_subtest {
my ($subscribecb, $want, $msg) = @_;
my @events = events_for(
$subscribecb,
'window');
my @focus = map { $_->{container}->{window} } grep { $_->{change} eq 'focus' } @events;
is_deeply(\@focus, $want, $msg);
}
xtest_button_press(2, 3, 3);
xtest_button_release(2, 3, 3);
sync_with_i3;
$con = $cv->recv;
is($con->{window}, $right->{id}, 'button 2 moves focus right');
reset_test;
subtest 'button 1 moves focus left', \&focus_subtest,
sub {
xtest_button_press(1, 3, 3);
xtest_button_release(1, 3, 3);
xtest_sync_with_i3;
},
[ $left->{id} ],
'button 1 moves focus left';
xtest_button_press(3, 3, 3);
xtest_button_release(3, 3, 3);
sync_with_i3;
$con = $cv->recv;
is($con->{window}, $left->{id}, 'button 3 moves focus left');
reset_test;
subtest 'button 2 moves focus right', \&focus_subtest,
sub {
xtest_button_press(2, 3, 3);
xtest_button_release(2, 3, 3);
xtest_sync_with_i3;
},
[ $right->{id} ],
'button 2 moves focus right';
xtest_button_press(4, 3, 3);
xtest_button_release(4, 3, 3);
sync_with_i3;
$con = $cv->recv;
is($con->{window}, $right->{id}, 'button 4 moves focus right');
reset_test;
subtest 'button 3 moves focus left', \&focus_subtest,
sub {
xtest_button_press(3, 3, 3);
xtest_button_release(3, 3, 3);
xtest_sync_with_i3;
},
[ $left->{id} ],
'button 3 moves focus left';
xtest_button_press(5, 3, 3);
xtest_button_release(5, 3, 3);
sync_with_i3;
$con = $cv->recv;
is($con->{window}, $left->{id}, 'button 5 moves focus left');
reset_test;
subtest 'button 4 moves focus right', \&focus_subtest,
sub {
xtest_button_press(4, 3, 3);
xtest_button_release(4, 3, 3);
xtest_sync_with_i3;
},
[ $right->{id} ],
'button 4 moves focus right';
subtest 'button 5 moves focus left', \&focus_subtest,
sub {
xtest_button_press(5, 3, 3);
xtest_button_release(5, 3, 3);
xtest_sync_with_i3;
},
[ $left->{id} ],
'button 5 moves focus left';
done_testing;