]> git.sur5r.net Git - i3/i3/commitdiff
Implement the tick event
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 24 Sep 2017 13:40:30 +0000 (15:40 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 30 Sep 2017 09:41:57 +0000 (11:41 +0200)
This makes our tests less flaky, shorter, and more readable.

fixes #2988

29 files changed:
AnyEvent-I3/lib/AnyEvent/I3.pm
docs/ipc
i3-msg/main.c
include/i3/ipc.h
include/ipc.h
src/ipc.c
testcases/lib/i3test.pm.in
testcases/lib/i3test/XTEST.pm
testcases/t/115-ipc-workspaces.t
testcases/t/199-ipc-mode-event.t
testcases/t/205-ipc-windows.t
testcases/t/219-ipc-window-focus.t
testcases/t/220-ipc-window-title.t
testcases/t/225-ipc-window-fullscreen.t
testcases/t/227-ipc-workspace-empty.t
testcases/t/231-ipc-floating-event.t
testcases/t/238-ipc-binding-event.t
testcases/t/257-keypress-group1-fallback.t
testcases/t/258-keypress-release.t
testcases/t/263-config-reload-reverts-bind-mode.t
testcases/t/265-ipc-mark.t
testcases/t/275-ipc-window-close.t
testcases/t/276-ipc-window-move.t
testcases/t/277-ipc-window-urgent.t
testcases/t/289-ipc-shutdown-event.t
testcases/t/290-keypress-numlock.t
testcases/t/514-ipc-workspace-multi-monitor.t
testcases/t/517-regress-move-direction-ipc.t
testcases/t/525-i3bar-mouse-bindings.t

index 08b1c0a7165bc1290800b8b2bd9a3e361077adc5..198c41c9a5eef1164a128150ec26496783134e69 100644 (file)
@@ -99,11 +99,12 @@ use constant TYPE_GET_BAR_CONFIG => 6;
 use constant TYPE_GET_VERSION => 7;
 use constant TYPE_GET_BINDING_MODES => 8;
 use constant TYPE_GET_CONFIG => 9;
+use constant TYPE_SEND_TICK => 10;
 
 our %EXPORT_TAGS = ( 'all' => [
     qw(i3 TYPE_RUN_COMMAND TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
        TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
-       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG)
+       TYPE_GET_BINDING_MODES TYPE_GET_CONFIG TYPE_SEND_TICK)
 ] );
 
 our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
@@ -120,6 +121,7 @@ my %events = (
     barconfig_update => ($event_mask | 4),
     binding => ($event_mask | 5),
     shutdown => ($event_mask | 6),
+    tick => ($event_mask | 7),
     _error => 0xFFFFFFFF,
 );
 
@@ -519,6 +521,18 @@ sub get_config {
     $self->message(TYPE_GET_CONFIG);
 }
 
+=head2 send_tick
+
+Sends a tick event. Requires i3 >= 4.15
+
+=cut
+sub send_tick {
+    my ($self, $payload) = @_;
+
+    $self->_ensure_connection;
+
+    $self->message(TYPE_SEND_TICK, $payload);
+}
 
 =head2 command($content)
 
index 526c8e696cdb6ff834232b7a99ee63c2b98049bc..8b767adebffa127bab432c2c9607b57ce491567b 100644 (file)
--- a/docs/ipc
+++ b/docs/ipc
@@ -64,6 +64,7 @@ to do that).
 | 7 | +GET_VERSION+ | <<_version_reply,VERSION>> | Gets the i3 version.
 | 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes.
 | 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config.
+| 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload.
 |======================================================
 
 So, a typical message could look like this:
@@ -126,6 +127,8 @@ BINDING_MODES (8)::
         Reply to the GET_BINDING_MODES message.
 GET_CONFIG (9)::
        Reply to the GET_CONFIG message.
+TICK (10)::
+       Reply to the SEND_TICK message.
 
 [[_command_reply]]
 === COMMAND reply
@@ -637,6 +640,19 @@ which is a string containing the config file as loaded by i3 most recently.
 { "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" }
 -------------------
 
+[[_tick_reply]]
+=== TICK reply
+
+The reply is a map containing the "success" member. After the reply was
+received, the tick event has been written to all IPC connections which subscribe
+to tick events. UNIX sockets are usually buffered, but you can be certain that
+once you receive the tick event you just triggered, you must have received all
+events generated prior to the +SEND_TICK+ message (happened-before relation).
+
+*Example:*
+-------------------
+{ "success": true }
+-------------------
 
 == Events
 
@@ -694,6 +710,10 @@ binding (5)::
        mouse
 shutdown (6)::
        Sent when the ipc shuts down because of a restart or exit by user command
+tick (7)::
+       Sent when the ipc client subscribes to the tick event (with +"first":
+       true+) or when any ipc client sends a SEND_TICK message (with +"first":
+       false+).
 
 *Example:*
 --------------------------------------------------------------------
@@ -866,6 +886,27 @@ because of a user action such as a +restart+ or +exit+ command. The +change
 }
 ---------------------------
 
+=== tick event
+
+This event is triggered by a subscription to tick events or by a +SEND_TICK+
+message.
+
+*Example (upon subscription):*
+--------------------------------------------------------------------------------
+{
+ "first": true,
+ "payload": ""
+}
+--------------------------------------------------------------------------------
+
+*Example (upon +SEND_TICK+ with a payload of +arbitrary string+):*
+--------------------------------------------------------------------------------
+{
+ "first": false,
+ "payload": "arbitrary string"
+}
+--------------------------------------------------------------------------------
+
 == See also (existing libraries)
 
 [[libraries]]
index 8907a6f7adf80cf68729983d264748505040b732..a4f948500f16c0749b2548704c5d3289bc9e9fda 100644 (file)
@@ -207,9 +207,11 @@ int main(int argc, char *argv[]) {
                 message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
             } else if (strcasecmp(optarg, "get_config") == 0) {
                 message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
+            } else if (strcasecmp(optarg, "send_tick") == 0) {
+                message_type = I3_IPC_MESSAGE_TYPE_SEND_TICK;
             } else {
                 printf("Unknown message type\n");
-                printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config\n");
+                printf("Known types: run_command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config, send_tick\n");
                 exit(EXIT_FAILURE);
             }
         } else if (o == 'q') {
index 993a2a2482d1de70ed67433ae2189948c6f53f35..9e0280c9363322b48ef7f25534bfde2b9ccd9c4f 100644 (file)
@@ -60,6 +60,9 @@ typedef struct i3_ipc_header {
 /** Request the raw last loaded i3 config. */
 #define I3_IPC_MESSAGE_TYPE_GET_CONFIG 9
 
+/** Send a tick event to all subscribers. */
+#define I3_IPC_MESSAGE_TYPE_SEND_TICK 10
+
 /*
  * Messages from i3 to clients
  *
@@ -74,6 +77,7 @@ typedef struct i3_ipc_header {
 #define I3_IPC_REPLY_TYPE_VERSION 7
 #define I3_IPC_REPLY_TYPE_BINDING_MODES 8
 #define I3_IPC_REPLY_TYPE_CONFIG 9
+#define I3_IPC_REPLY_TYPE_TICK 10
 
 /*
  * Events from i3 to clients. Events have the first bit set high.
@@ -101,3 +105,6 @@ typedef struct i3_ipc_header {
 
 /** The shutdown event will be triggered when the ipc shuts down */
 #define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)
+
+/** The tick event will be sent upon a tick IPC message */
+#define I3_IPC_EVENT_TICK (I3_IPC_EVENT_MASK | 7)
index 7ffbf7a831f9de3418f9d4ddad304beee5cbff22..c6ad35c770745038eb99dbd370966fd489bc262f 100644 (file)
@@ -31,6 +31,10 @@ typedef struct ipc_client {
     int num_events;
     char **events;
 
+    /* For clients which subscribe to the tick event: whether the first tick
+     * event has been sent by i3. */
+    bool first_tick_sent;
+
     TAILQ_ENTRY(ipc_client)
     clients;
 } ipc_client;
index 759665fe91b76341e74128807ce830c39e0ef63e..99b9e0ec65814fd7c4fb52f5ef724f5c2344c3c3 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -1046,8 +1046,9 @@ static int add_subscription(void *extra, const unsigned char *s,
     memcpy(client->events[event], s, len);
 
     DLOG("client is now subscribed to:\n");
-    for (int i = 0; i < client->num_events; i++)
+    for (int i = 0; i < client->num_events; i++) {
         DLOG("event %s\n", client->events[i]);
+    }
     DLOG("(done)\n");
 
     return 1;
@@ -1099,6 +1100,25 @@ IPC_HANDLER(subscribe) {
     yajl_free(p);
     const char *reply = "{\"success\":true}";
     ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
+
+    if (client->first_tick_sent) {
+        return;
+    }
+
+    bool is_tick = false;
+    for (int i = 0; i < client->num_events; i++) {
+        if (strcmp(client->events[i], "tick") == 0) {
+            is_tick = true;
+            break;
+        }
+    }
+    if (!is_tick) {
+        return;
+    }
+
+    client->first_tick_sent = true;
+    const char *payload = "{\"first\":true,\"payload\":\"\"}";
+    ipc_send_message(client->fd, strlen(payload), I3_IPC_EVENT_TICK, (const uint8_t *)payload);
 }
 
 /*
@@ -1122,9 +1142,35 @@ IPC_HANDLER(get_config) {
     y(free);
 }
 
+/*
+ * Sends the tick event from the message payload to subscribers. Establishes a
+ * synchronization point in event-related tests.
+ */
+IPC_HANDLER(send_tick) {
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("payload");
+    yajl_gen_string(gen, (unsigned char *)message, message_size);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("tick", I3_IPC_EVENT_TICK, (const char *)payload);
+    y(free);
+
+    const char *reply = "{\"success\":true}";
+    ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_TICK, (const uint8_t *)reply);
+    DLOG("Sent tick event\n");
+}
+
 /* The index of each callback function corresponds to the numeric
  * value of the message type (see include/i3/ipc.h) */
-handler_t handlers[10] = {
+handler_t handlers[11] = {
     handle_run_command,
     handle_get_workspaces,
     handle_subscribe,
@@ -1135,6 +1181,7 @@ handler_t handlers[10] = {
     handle_get_version,
     handle_get_binding_modes,
     handle_get_config,
+    handle_send_tick,
 };
 
 /*
index a484c91a441fe318cbecfe5f3286e2c9e094ba76..5b24ab39144a28c8422201092eda9ca95c074a2a 100644 (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 we’ll 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>
index 40759cfdb1098942358c2e61d6bc7bdcc5ef7c13..5c1dfe13ac1bd4f879fea12a71ebffe196172e05 100644 (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
index 34ce078130c34e0e99bd98558d6e7c6f1370ba1d..b0c4354ec10fe7385e41ed5a95d7dd06b3e01051 100644 (file)
 
 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;
index 959ff6c474281260ab8d0e2b59133bc04abc21f3..0e4f89600c47b39686609e6456e2bd652bd48a18 100644 (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;
index ca7db1533493416550d1f984f626f8cc796fc406..bafd155f6afeecf055bd0efc5579ad667c8ee174 100644 (file)
 
 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 $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');
+my @events = events_for(
+    sub { open_window },
+    'window');
 
-}
+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;
index ae7781877cffac9e3e7a26c5d2d0a7a123bd76be..5baf68a80163a200110cbe340f1571730da9ce4d 100644 (file)
 
 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');
-
-$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');
-
-$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');
+sub focus_subtest {
+    my ($cmd, $name) = @_;
 
-$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');
+    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 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;
index c751350a43f9660150323eaed2077f0dc040cabe..b5d14e237864622f64dea8fa8aec4643d2d26db5 100644 (file)
 
 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;
-
-$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');
+my @events = events_for(
+    sub {
+       $window->name('New Window Title');
+       sync_with_i3;
+    },
+    'window');
 
-}
+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;
index aeabe9539f44cbb5a3f9040a1e7a49d1fd34372a..bc150546906e94e625e0c3a6fb8d920aa2480399 100644 (file)
 # 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;
index fe8e03c02b01f9cb37c335fd2097a0e29ac8547c..b1f517ef5e4eb67aed9cf44044b02c04a31686db 100644 (file)
 #
 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;
-
-    cmd "workspace $ws2";
-
-    sync_with_i3;
+    my @events = events_for(
+       sub { cmd "workspace $ws2" },
+       'workspace');
 
-    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;
-
-    # 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";
+    my @events = events_for(
+       sub { cmd "workspace $ws2" },
+       'workspace');
 
-    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;
-
-    cmd '[id="' . $w1->id . '"] kill';
-
-    sync_with_i3;
+    my @events = events_for(
+       sub {
+           $w1->unmap;
+           sync_with_i3;
+       },
+       'workspace');
 
-    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;
index e38a187609797a1853fca76f5a590aeca083fc38..96c94a497bd7b1b1a00f0b440ff7acc8a229a192 100644 (file)
 # 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;
index af3f4d2fa10bd25f6a13e2bdf028f5f96d2226eb..bec95a2376e29fcb2cd8952c718482262c8c614e 100644 (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 = AE::cv;
-    my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
-
-    $i3->subscribe({
-            binding => sub {
-                $cv->send(shift);
-            }
-        })->recv;
-
-    qx(xdotool key $binding_symbol);
-
-    my $e = $cv->recv;
-
-    does_i3_live;
+    my $cv = AnyEvent->condvar;
 
-    diag "Event:\n", Dumper($e);
+    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');
 
-    ok($e,
-        'the binding event should emit when user input triggers an i3 binding event');
+    is(scalar @events, 1, 'Received 1 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);
index e3874c92b2ad27ffeedb9e5bed4ce05ae2d96dff..bc08aa2f2118997b2117e564bc633372126db65d 100644 (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|);
 
index bf446003b38ab6d1d27e67600eb989e6bdb7c96d..8bca0d869996216c7aa4293e82703259841408bc 100644 (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;
index ba95897c2974ffaf8e392ce40f79b0d6af2a34cd..1416b6b9e19bedf3d0e0b46feefa6c837d92413b 100644 (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;
index 06d8d83d15cad1bca951e84c76f5a409658acfaf..a101944e793e83233e3addc456afa9927101038e 100644 (file)
 # 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';
 
 ###############################################################################
 
index bf9d4f8bba6cfca833b930dcbbbe3e7b05fbb780..ccaf4440ea8a0d9dbe8b64543984e28d53c13751 100644 (file)
 # 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;
index a3c825348a203246b736441600ecf3b48395da48..f3606b4e8106b1c32d34a463cca45bb2d29d5d24 100644 (file)
 # 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;
-
-ok($con, 'moving a window should emit the window::move event');
-is($con->{window}, $window->{id}, 'the event should contain info about the window');
+sub move_subtest {
+    my ($cmd) = @_;
+    my $cv = AnyEvent->condvar;
+    my @events = events_for(
+       sub { cmd $cmd },
+       'window');
 
-reset_test;
-
-cmd 'move to workspace ws_new';
-$con = $cv->recv;
+    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');
+}
 
-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;
index 2af29dac9de05494c71cef31d606ec535240ea6a..4eea2cdcac1ef27eed5a40a0cc25ec5bca6fbd7f 100644 (file)
 #
 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;
-
-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');
-
-$cv = AnyEvent->condvar;
-$win->delete_hint('urgency');
-$event = $cv->recv;
-
-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');
+sub urgency_subtest {
+    my ($subscribecb, $win, $want) = @_;
+
+    my @events = events_for(
+       $subscribecb,
+       'window');
+
+    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");
+}
+
+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;
index 0cf347aa88b923c210f0c551308ec05e67c3edbb..f2b5246010054fcc6a0825918bd52007229c035e 100644 (file)
 # 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;
index b896e4320f1b2d46b6611c373c2b36f4767581ba..94a5747d0ffa9e5f4033ce71b0fab2a8c551b60b 100644 (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,14 +340,11 @@ 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');
 
-# TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
+is(scalar @unexpected2, 0, 'Did not trigger the KP_Down keybinding with KP_2');
 
-sync_with_i3;
-is(scalar @i3test::XTEST::binding_events, 18, 'Received exactly 18 binding events');
+# TODO: This test does not verify that i3 does _NOT_ grab keycode 87 with Mod2.
 
 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
index e3753bec681ffffa3ac15ac2814d0c1b76e38d3c..ac918fe379d74c160fed7c672156f47edf5af34f 100644 (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;
index 217d898ca2f2dbdb202368920dcbf8ee03a847f6..2f7f2b27f897b5168a4fa839071f9d7614bf2430 100644 (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;
 
-# move a window over to the right output
-cmd 'move right';
-my $event = $focus->recv;
+sub focus_subtest {
+    my ($cmd, $want) = @_;
 
-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');
+    my @events = events_for(
+       sub { cmd $cmd },
+       'workspace');
 
-# reset and try again
-$focus = AnyEvent->condvar;
-cmd 'workspace ws-left';
-$focus->recv;
+    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
+subtest 'move right (1)', \&focus_subtest, 'move right', 1;
 
-$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');
+# move another window
+cmd 'workspace ws-left';
+subtest 'move right (2)', \&focus_subtest, 'move right', 2;
 
 done_testing;
index ff34328c6c35ee2ecbc727b07dd6aecfbc08d5f0..1dfa6aa71e25cfb01553bdbc0aef9d0fc1f3130c 100644 (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;
-
-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;
-
-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;
 
-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;
+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(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 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';
+
+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';
+
+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';
+
+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;