Test2:...Plugin(3pm)

✨ NAME ✨

Test2::Harness::Plugin β€” the base class for Test2::Harness plugins. That's the 3pm page, bestie, and yes we're getting into the plumbing today. 🫢


🌸 DESCRIPTION 🌸

Okay so real talk: this class holds the methods that are specific to Test2::Harness, which is the backend. But like… most of the time you don't actually want this one, you want to subclass App::Yath::Plugin β€” that one subclasses this one AND brings extra methods that apply to yath (the UI layer). So it's giving "starter pack, but the deluxe version is right there." πŸ’…


πŸŽ€ SYNOPSIS πŸŽ€

Fr fr, you probably wanna subclass App::Yath::Plugin instead. This class here mainly exists to separate concerns, and is not something you should use directly. It's the responsible older sibling, not the main character.

package Test2::Harness::Plugin::MyPlugin;

use parent 'Test2::Harness::Plugin';

# ... Define methods

1;

πŸ’– METHODS πŸ’–

$plugin->munge_search($input, $default_search, $settings)

Okay so the three things you're getting here:

  • $input is an arrayref of files and/or directories that got handed over at the command line.
  • $default_search is an arrayref full of the default files/directories that get pulled in when nothing is specified at the command line.
  • $settings is an instance of Test2::Harness::Settings.

$undef_or_inst = $plugin->claim_file($path, $settings)

This is your moment to ✨claim✨ a test file early β€” like, before Test2::Harness even gets to it. Not feeling it? Just return undef and move on, no drama. Wanna claim it? Then return an instance of Test2::Harness::TestFile that you built with $path. Iconic either way. πŸ’…

$plugin->munge_files(\@tests, $settings)

Here's your opportunity to go in and modify the data for any test file that's about to be run. First argument = an arrayref of Test2::Harness::TestFile objects. Go off. πŸ”₯

$hashref = $plugin->duration_data($settings, $test_names)

If it's defined, this can hand back a hashref of duration data. No duration data to give? Return undef. And heads up: the FIRST plugin listed that provides duration data WINS β€” once duration data has been obtained, no other plugins get checked. Not even a peek. πŸ‘‘

Example duration data, looking cute:

{
    't/foo.t' => 'medium',
    't/bar.t' => 'short',
    't/baz.t' => 'long',
}

$hashref_or_arrayref = $plugin->coverage_data(\@changed)

$hashref_or_arrayref = $plugin->coverage_data()

Same energy, coverage edition. If defined, this can return a hashref of all coverage data, or an arrayref of the tests that cover the tests listed in @changed. Nothing available? Return undef, no cap. And again: the first plugin to provide coverage data WINS, and once coverage data has been obtained, no other plugins will be checked.

Examples:

[
    'foo.t',
    'bar.t',
    'baz.t',
]
{
    'lib/Foo.pm' => [
        't/foo.t',
        't/integration.t',
    ],
    'lib/Bar.pm' => [
        't/bar.t',
        't/integration.t',
    ],
}

$plugin->post_process_coverage_tests($settings, \@tests)

This is your plugin's chance to do post-processing on the list of coverage tests that are gonna run. It's mainly useful for removing duplicates when multiple plugins add coverage data, or merging entries where applicable. The way this works: it gets called after all plugins have generated their coverage test list. So everybody's done talking, now we tidy up. 🧹

Oh and btw β€” plugins may implement this without implementing coverage_data(). That makes it lowkey perfect if you wanna use a pre-existing coverage module and just do post-processing on whatever it gives you. Slay.

$plugin->inject_run_data(meta => $meta, fields => $fields, run => $run)

A callback that lets your plugin add meta-data or custom fields to the run event. Both the meta-data and the fields show up in the event log, and they're particularily useful to App::Yath::UI. πŸ’œ

sub inject_run_data {
    my $class  = shift;
    my %params = @_;

    my $meta   = $params{meta};
    my $fields = $params{fields};

    # Meta-data is a hash, each plugin should define its own key, and put
    # data under that key
    $meta->{MyPlugin}->{stuff} = "Stuff!";

    # Fields is an array of fields that a UI might want to display when showing the run.
    push @$fields => {name => 'MyPlugin', details => "Human Friendly Stuff", raw => "Less human friendly stuff", data => $all_the_stuff};

    return;
}

$plugin->setup($settings)

A callback so you can run setup logic when the runner starts. ⚠️ Note this one: in a persistent runner, this runs once on startup β€” it does not run for each "run" command against the persistent runner. So don't be out here expecting it to fire per run, bestie.

$plugin->teardown($settings)

Same deal, other end. A callback that lets you run teardown logic when the runner stops. And again for the persistent runner girlies: this runs once on termination, it is not run for each "run" command against the persistent runner.

@files = $plugin->changed_files($settings)

Grab a list of files that have changed. "Changed" is whatever your plugin decides it means β€” plugins are free to define that however they like. The finder might use this to figure out what tests to run, based on coverage data collected in previous runs. πŸ”

Heads up: data from all changed_files() calls from all plugins gets merged. Everybody's invited to the group chat.

($type, $value) = $plugin->changed_diff($settings)

Generate a diff that can be used to calculate changed files/subs, which is what you run tests on. But unlike changed_files(), only 1 diff will be used β€” first plugin listed that returns one wins. Periodt. And this doesn't run at all if a diff is provided via --changed-diff.

Diffs must be in the same format as this git command:

git diff -U1000000 -W --minimal BASE_BRANCH_OR_COMMIT

Some other diff formats may work by chance, but they are not dirfectly supported (and yes, the og docs said "dirfectly", we're keeping the typo it's iconic πŸ’…). In the future other diff formats may be directly supported, but not yet.

The following return sets are allowed:

  • file => string β€” path to a diff file.
  • diff => string β€” in memory diff as a single string.
  • lines => \@lines β€” diff where each line is a seperate string in an arrayref.
  • line_sub => sub { ... } β€” sub that returns one line per call and undef when there are no more lines.
  • handle => $FH β€” a filehandle to the diff.

$exit = $plugin->shellcall($settings, $name, $cmd)

$exit = $plugin->shellcall($settings, $name, @cmd)

Basically system(), except STDERR and STDOUT get redirected to files that the yath collector will pick up β€” so any output from the command shows up as events and becomes part of the yath log. πŸŽ€ If no workspace is available though, this will not redirect IO and it'll be identical to calling system().

This is particularily useful inside setup() and teardown() when you're running external commands β€” specially any that daemonize and keep producing output after the setup/teardown method has already finished. 😭

$name is required bestie, because it gets used for filenames AND as the output tag. Best to limit it to 8 characters.

$plugin->redirect_io($settings, $name)

🚨 WARNING: This must NEVER be called in a primary yath process. Only use this in forked processes that you control. If this is used in a main process it could hide ALL output. All of it. Gone. πŸ’€

This redirects STDERR and STDOUT to files that'll be picked up by the yath collector, so any output appears as proper yath events and gets included in the yath log.

$name is required because it will be used for filenames, and will be used as the output tag (best to limit it to 8 characters).

$plugin->TO_JSON

This is here as a bare minimum serialization method. It returns the plugin class name. That's it, that's the whole vibe. πŸ¦‹


🌈 SOURCE 🌈

The source code repository for Test2-Harness can be found at http://github.com/Test-More/Test2-Harness/.


πŸ‘‘ MAINTAINERS πŸ‘‘

Chad Granum <exodist@cpan.org>


🌟 AUTHORS 🌟

Chad Granum <exodist@cpan.org>


Copyright 2020 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 β€” perl v5.40.1, dated 2026-05-10, from the User Contributed Perl Documentation collection. Go forth and plugin. πŸ’…βœ¨

πŸ“¦ where this page comes from