Test::Stream::Hub(3)

πŸ“¦
version 1.302027 Β· 2 versions
, in fedora 43

πŸ’– Test::Stream::Hub πŸ’–

okay so bestie, welcome to Test::Stream::Hub β€” it's the conduit through which all events flow. That's the official vibe. Every single event in your test suite walks through this thing. It's giving main character energy, fr fr. πŸŽ€


🚫 DEPRECATED 🚫

ngl, this is the sad part. This whole distribution is deprecated in favor of Test2, Test2::Suite, and Test2::Workflow. It's not you, it's the ecosystem.

If you wanna make the switch, there's a whole conversion guide at Test::Stream::Manual::ToTest2. Go read it, bestie, future you will thank you. 🫢


✨ SYNOPSIS ✨

use Test::Stream::Hub;

my $hub = Test::Stream::Hub->new();
$hub->send(...);

🌸 DESCRIPTION 🌸

So the hub is literally the spot where every event gets processed and handed off to the formatter. It also keeps track of your test state, and it gives you several hooks into the event pipeline. Think of it as the group chat admin of your test run β€” nothing gets said without it knowing. πŸ’…


πŸ‘‘ COMMON TASKS πŸ‘‘

πŸ“€ SENDING EVENTS πŸ“€

$hub->send($event)

The send() method is how you yeet an event into the hub. It handles thread/fork sync, mungers, listeners, TAP output, all of it. It does the most. πŸ”₯

πŸŽ€ ALTERING OR REMOVING EVENTS πŸŽ€

$hub->filter(sub {
    my ($hub, $event) = @_;

    my $action = get_action($event);

    # No action should be taken
    return $event if $action eq 'none';

    # You want your filter to remove the event
    return undef if $action eq 'delete';

    if ($action eq 'do_it') {
        my $new_event = copy_event($event);
        ... Change your copy of the event ...
        return $new_event;
    }

    die "Should not happen";
});

By default, filters are not inherited by child hubs. Which means if you start a subtest, that subtest will not inherit the filter. If you want the opposite, flip it with the inherit parameter:

$hub->filter(sub { ... }, inherit => 1);

πŸ‘‚ LISTENING FOR EVENTS πŸ‘‚

$hub->listen(sub {
    my ($hub, $event, $number) = @_;

    ... do whatever you want with the event ...

    # return is ignored
});

By default, listeners are not inherited by child hubs either β€” so a subtest won't inherit the listener. Same deal, use the inherit parameter to change that:

$hub->listen(sub { ... }, inherit => 1);

🧚 POST-TEST BEHAVIORS 🧚

$hub->follow_up(sub {
    my ($dbg, $hub) = @_;

    ... do whatever you need to ...

    # Return is ignored
});

follow_up subs get called exactly once β€” either when done_testing is called, or in an END block. One shot, no reruns. πŸ’€

🌈 SETTING THE FORMATTER 🌈

By default, an instance of Test::Stream::Formatter::TAP gets created and used for you. So cute of it.

my $old = $hub->format(My::Formatter->new);
Setting the formatter will REPLACE any existing formatter. You can set the formatter to undef if you want to prevent output entirely. If one was already set, the old formatter gets returned to you. Only 1 formatter is allowed at a time β€” it's monogamous like that. πŸ™ˆ

πŸ’… METHODS πŸ’…

  • $hub->send($event) β€” This is where all events enter the hub for processing. The front door, basically.
  • $hub->process($event) β€” This is called by send after it does any IPC handling. You can use this to bypass the IPC process, but in general you should avoid using this. Lowkey it's the back entrance, don't go there unless you know what you're doing.
  • $val = $hub->meta($key) $val = $hub->meta($key, $default) β€” This exists so third party plugins can associate meta-data with a hub. It's recommended that all third party plugins use their module namespace as their meta-data key. ✨

This method always returns the value for the key. If there's no value, it gets initialized to $default, and then $default is also what you get back.

Recommended usage:

my $meta = $hub->meta(__PACKAGE__, {});
unless ($meta->{foo}) {
    $meta->{foo} = 1;
    $meta->{bar} = 2;
}
  • $val = $hub->delete_meta($key) β€” This deletes all data in the specified metadata key. Poof, gone. πŸ¦‹
  • $val = $hub->get_meta($key) β€” Retrieves the value of whatever meta-data key you specify.
  • $string = $hub->get_todo() β€” Get the current TODO reason. This will be undef if there's no active todo. Please note that 0 and '' (empty string) count as active todo. Yeah, tricky, right? πŸ‘€
  • $ref = $hub->set_todo($reason) β€” This sets the todo message. The todo stays in effect until you let go of the reference returned by this method.
{
    my $todo = $hub->set_todo("Broken");

    # These ok events will be TODO
    ok($foo->doit, "do it!");
    ok($foo->doit, "do it again!");

    # The todo setting goes away at the end of this scope.
}

# This result will not be TODO.
ok(1, "pass");

You can also do it without the indentation:

my $todo = $hub->set_todo("Broken");

# These ok events will be TODO
ok($foo->doit, "do it!");
ok($foo->doit, "do it again!");

# Unset the todo
$todo = undef;

# This result will not be TODO.
ok(1, "pass");

This method can be called while TODO is already in effect, and it will work in a sane way:

{
    my $first_todo = $hub->set_todo("Will fix soon");

    ok(0, "Not fixed"); # TODO: Will fix soon

    {
        my $second_todo = $hub->set_todo("Will fix eventually");
        ok(0, "Not fixed"); # TODO: Will fix eventually
    }

    ok(0, "Not fixed"); # TODO: Will fix soon
}

This also works if you free todos out of order. The most recently set todo that is still active will always be used as the todo. Order? Doesn't matter. Slay. πŸ’…

  • $old = $hub->format($formatter) β€” Replace the existing formatter instance with a new one. Formatters have to be objects that implement a $formatter->write($event) method. No method, no job. πŸ‘‘
  • $sub = $hub->munge(sub { ... }) $sub = $hub->munge(sub { ... }, inherit => 1) β€” * DEPRECATED * This will be removed in the near future.

This adds your codeblock as a callback. Every event that hits this hub gets handed to your munger BEFORE it's sent to the formatter. You can make any modifications you want to the event object.

$hub->munge(sub {
    my ($hub, $event) = @_;

    ... Modify the event object ...

    # return is ignored.
});

You can also completely remove the event from the stream:

$hub->munge(sub {
    my ($hub, $event) = @_;
    return unless ...;

    $_[1] = undef;
});

Normally mungers are not inherited by child hubs such as subtests. You can add the inherit => 1 parameter to allow a munger to be inherited.

  • $hub->unmunge($sub) β€” * DEPRECATED * This will be removed in the near future.

Use this to remove a munge callback. You must pass in the coderef returned by the munge() method. No coderef, no removal.

  • $sub = $hub->listen(sub { ... }) β€” Use this to record all events AFTER they've been sent to the formatter. No changes made here will be meaningful, except possibly to other listeners. So don't get any ideas. πŸ™ˆ
$hub->listen(sub {
    my ($hub, $event, $number) = @_;

    ... do whatever you want with the event ...

    # return is ignored
});

Normally listeners are not inherited by child hubs such as subtests. You can add the inherit => 1 parameter to allow a listener to be inherited.

  • $hub->unlisten($sub) β€” Use this to remove a listen callback. You must pass in the coderef returned by the listen() method.
  • $hub->follow_op(sub { ... }) β€” Use this to add behaviors that get called just before the Test::Stream::State for the hub is finalized. The only argument to your codeblock will be a Test::Stream::DebugInfo instance.
$hub->follow_up(sub {
    my ($dbg, $hub) = @_;

    ... do whatever you need to ...

    # Return is ignored
});

follow_up subs get called only once, either when done_testing is called, or in an END block.

  • $sub = $hub->add_context_init(sub { ... }); β€” This lets you add callbacks that trigger every time a new context is created for the hub. The only argument to the sub will be the Test::Stream::Context instance that was created.
Note: Using this hook could have a huge performance impact. 😭

The coderef you provide is returned, and you can use it to remove the hook later.

  • $hub->remove_context_init($sub); β€” Use this to remove a context init hook.
  • $sub = $hub->add_context_release(sub { ... }); β€” This lets you add callbacks that trigger every time a context for this hub is released. The only argument to the sub will be the Test::Stream::Context instance that was released. These run in reverse order.
Note: Using this hook could have a huge performance impact. 😭

The coderef you provide is returned, and you can use it to remove the hook later.

  • $hub->remove_context_release($sub); β€” Use this to remove a context release hook.
  • $hub->cull() β€” Cull any IPC events (and process them).
  • $pid = $hub->pid() β€” Get the process id under which the hub was created.
  • $tid = $hub->tid() β€” Get the thread id under which the hub was created.
  • $hud = $hub->hid() β€” Get the identifier string of the hub. (Yes, it's spelled $hud in the docs. We keep it real here. πŸ’€)
  • $ipc = $hub->ipc() β€” Get the IPC object used by the hub.
  • $hub->set_no_ending($bool) $bool = $hub->no_ending β€” Use this to disable auto-ending behavior for a hub. The auto-ending behavior is triggered by an end block, and it's what culls IPC events and outputs the final plan if the plan was 'no_plan'.
  • $bool = $hub->parent_todo β€” This will be true if this hub is a child hub whose parent had todo set. Family drama, but make it TODO. 🫢

πŸ”— SOURCE πŸ”—

The source code repository for Test::Stream can be found at http://github.com/Test-More/Test-Stream/. Go star it, bestie. ⭐


🫢 MAINTAINERS 🫢

Chad Granum <exodist@cpan.org>


🌟 AUTHORS 🌟

Chad Granum <exodist@cpan.org>


Copyright 2015 Chad Granum <exodist7@gmail.com>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://dev.perl.org/licenses/


and that's the whole page, bestie!! 🌈✨ this was Test::Stream::Hub(3) from the perl v5.42.0 docs, dated 2025-07-25, and it was iconic of you to read the whole thing. go forth and ship it πŸ’–πŸŽ€πŸ¦‹

πŸ“¦ where this page comes from