1package Test2::Harness::Runner::Job;
2use strict;
3use warnings;
4
5our $VERSION = '1.000082';
6
7use Carp qw/confess croak/;
8use Config qw/%Config/;
9use Scalar::Util qw/weaken blessed/;
10use Test2::Util qw/CAN_REALLY_FORK/;
11use Time::HiRes qw/time/;
12
13use File::Spec();
14use File::Temp();
15
16use Test2::Harness::Util qw/fqmod clean_path write_file_atomic write_file mod2file open_file parse_exit process_includes chmod_tmp/;
17use Test2::Harness::IPC;
18
19use parent 'Test2::Harness::IPC::Process';
20use Test2::Harness::Util::HashBase(
21    qw{ <task <runner <run <settings }, # required
22    qw{
23        <fork_callback
24        <last_output_size
25        +output_changed
26
27        +verbose
28
29        +via
30
31        +run_dir +job_dir +tmp_dir +event_dir
32
33        +ch_dir +unsafe_inc
34
35        +use_fork +use_w_switch
36
37        +includes +runner_includes
38        +switches
39        +use_stream
40        +cli_includes
41        +cli_options
42
43        +smoke
44        +retry +retry_isolated +is_try
45
46        +args +file +run_file
47
48        +out_file +err_file +in_file +bail_file
49
50        +load +load_import
51
52        +event_uuids +mem_usage +io_events
53
54        +env_vars
55
56        +event_timeout +post_exit_timeout +use_timeout
57
58        +switches_from_env
59
60        +et_file +pet_file
61    }
62);
63
64sub category { 'job' }
65
66sub init {
67    my $self = shift;
68
69    croak "'runner' is a required attribute"   unless $self->{+RUNNER};
70    croak "'run' is a required attribute"      unless $self->{+RUN};
71    croak "'settings' is a required attribute" unless $self->{+SETTINGS};
72
73    delete $self->{+JOB_DIR};
74
75    # Avoid a ref cycle
76    #weaken($self->{+RUNNER});
77
78    my $task = $self->{+TASK} or croak "'task' is a required attribute";
79
80    delete $self->{+LAST_OUTPUT_SIZE};
81
82    confess "Task does not have a job ID" unless $task->{job_id};
83    confess "Task does not have a file"   unless $task->{file};
84}
85
86sub job_id { $_[0]->{+TASK}->{job_id} }
87
88sub prepare_dir {
89    my $self = shift;
90
91    $self->job_dir();
92    $self->tmp_dir();
93    $self->event_dir();
94}
95
96sub via {
97    my $self = shift;
98
99    return undef if $self->{+SETTINGS}->debug->dummy;
100
101    return $self->{+VIA} if exists $self->{+VIA};
102
103    my $task = $self->{+TASK};
104    return $self->{+VIA} = $task->{via} if $task->{via};
105
106    return $self->{+VIA} = $self->{+FORK_CALLBACK} if $self->{+FORK_CALLBACK} && $self->use_fork;
107
108    return $self->{+VIA} = undef;
109}
110
111sub spawn_params {
112    my $self = shift;
113
114    my $task = $self->{+TASK};
115
116    my $command;
117    if ($task->{binary} || $task->{non_perl}) {
118        my $file = $self->ch_dir ? $self->file : $self->rel_file;
119        $command = [clean_path($file), $self->args];
120    }
121    else {
122        $command = [
123            $^X,
124            $self->cli_includes,
125            $self->{+SETTINGS}->runner->nytprof ? ('-d:NYTProf') : (),
126            $self->switches,
127            $self->cli_options,
128
129            $self->{+SETTINGS}->debug->dummy ? ('-e', 'print "1..0 # SKIP dummy mode"') : (sub { $self->run_file }),
130
131            $self->args,
132        ];
133    }
134
135    my $out_fh = open_file($self->out_file, '>');
136    my $err_fh = open_file($self->err_file, '>');
137    my $in_fh  = open_file($self->in_file,  '<');
138
139    return {
140        command => $command,
141        stdin   => $in_fh,
142        stdout  => $out_fh,
143        stderr  => $err_fh,
144        chdir   => $self->ch_dir(),
145        env     => $self->env_vars(),
146    };
147}
148
149sub switches_from_env {
150    my $self = shift;
151
152    return @{$self->{+SWITCHES_FROM_ENV}} if $self->{+SWITCHES_FROM_ENV};
153
154    return @{$self->{+SWITCHES_FROM_ENV} = []} unless $ENV{HARNESS_PERL_SWITCHES};
155
156    return @{$self->{+SWITCHES_FROM_ENV} = [split /\s+/, $ENV{HARNESS_PERL_SWITCHES}]};
157}
158
159my %JSON_SKIP = (
160    SETTINGS()         => 1,
161    TASK()             => 1,
162    RUNNER()           => 1,
163    RUN()              => 1,
164    CLI_INCLUDES()     => 1,
165    CLI_OPTIONS()      => 1,
166    ERR_FILE()         => 1,
167    ET_FILE()          => 1,
168    EVENT_DIR()        => 1,
169    EXIT()             => 1,
170    EXIT_TIME()        => 1,
171    IN_FILE()          => 1,
172    JOB_DIR()          => 1,
173    LAST_OUTPUT_SIZE() => 1,
174    OUT_FILE()         => 1,
175    BAIL_FILE()        => 1,
176    OUTPUT_CHANGED()   => 1,
177    PET_FILE()         => 1,
178    RUN_DIR()          => 1,
179    TMP_DIR()          => 1,
180);
181
182sub TO_JSON {
183    my $self = shift;
184
185    my $out = { %{$self->{+TASK}} };
186
187    for my $attr (Test2::Harness::Util::HashBase::attr_list(blessed($self))) {
188        next if $JSON_SKIP{$attr};
189        $self->$attr unless defined $self->{$attr};
190        $out->{$attr} = $self->{$attr};
191    }
192
193    delete $out->{+FORK_CALLBACK};
194    delete $out->{+VIA} if ref($out->{+VIA}) eq 'CODE';
195
196    $out->{job_name} //= $out->{job_id};
197    $out->{abs_file} = clean_path($self->file);
198
199    return $out;
200}
201
202sub run_file  {
203    my $self = shift;
204    return $self->{+RUN_FILE} //= $self->rel_file;
205}
206
207sub rel_file  { File::Spec->abs2rel($_[0]->file) }
208sub file      { $_[0]->{+FILE}      //= clean_path($_[0]->{+TASK}->{file}, 0) }
209sub err_file  { $_[0]->{+ERR_FILE}  //= clean_path(File::Spec->catfile($_[0]->job_dir, 'stderr')) }
210sub out_file  { $_[0]->{+OUT_FILE}  //= clean_path(File::Spec->catfile($_[0]->job_dir, 'stdout')) }
211sub bail_file { $_[0]->{+BAIL_FILE} //= clean_path(File::Spec->catfile($_[0]->event_dir, 'bail')) }
212sub et_file   { $_[0]->{+ET_FILE}   //= clean_path(File::Spec->catfile($_[0]->job_dir, 'event_timeout')) }
213sub pet_file  { $_[0]->{+PET_FILE}  //= clean_path(File::Spec->catfile($_[0]->job_dir, 'post_exit_timeout')) }
214sub run_dir   { $_[0]->{+RUN_DIR}   //= clean_path(File::Spec->catdir($_[0]->{+RUNNER}->dir, $_[0]->{+RUN}->run_id)) }
215
216sub bailed_out {
217    my $self = shift;
218
219    if(-f $self->bail_file) {
220        my $fh = open_file($self->bail_file, '<');
221        my $reason = <$fh> || 1;
222        return $reason;
223    }
224
225    my $fh = open_file($self->out_file, '<');
226    while (my $line = <$fh>) {
227        next unless $line =~ m/^Bail out!\s*(.*)$/;
228        return $1 || 1;
229    }
230
231    return "";
232}
233
234sub output_size {
235    my $self = shift;
236
237    my $size = 0;
238
239    $size += -s $self->err_file || 0;
240    $size += -s $self->out_file || 0;
241
242    return $self->{+LAST_OUTPUT_SIZE} = $size;
243}
244
245sub output_changed {
246    my $self = shift;
247
248    my $last = $self->{+LAST_OUTPUT_SIZE};
249    my $size = $self->output_size();
250
251    # Output changed, update time
252    return $self->{+OUTPUT_CHANGED} = time() if $last && $size != $last;
253
254    # Return the last recorded time, if there is no previously recorded time then the record starts now
255    return $self->{+OUTPUT_CHANGED} //= time();
256}
257
258sub verbose { $_[0]->{+VERBOSE} //= $_[0]->{+TASK}->{verbose} // 0 }
259sub is_try  { $_[0]->{+IS_TRY}  //= $_[0]->{+TASK}->{is_try}  // 0 }
260sub ch_dir  { $_[0]->{+CH_DIR}  //= $_[0]->{+TASK}->{ch_dir}  // '' }
261sub unsafe_inc  { $_[0]->{+UNSAFE_INC}  //= $_[0]->{+RUNNER}->unsafe_inc }
262sub event_uuids { $_[0]->{+EVENT_UUIDS} //= $_[0]->run->event_uuids }
263sub mem_usage   { $_[0]->{+MEM_USAGE}   //= $_[0]->run->mem_usage }
264
265sub io_events { $_[0]->{+IO_EVENTS} //= $_[0]->_fallback(io_events => 1, qw/task run/) }
266
267sub smoke             { $_[0]->{+SMOKE}             //= $_[0]->_fallback(smoke             => 0,     qw/task/) }
268sub retry_isolated    { $_[0]->{+RETRY_ISOLATED}    //= $_[0]->_fallback(retry_isolated    => 0,     qw/task run/) }
269sub use_stream        { $_[0]->{+USE_STREAM}        //= $_[0]->_fallback(use_stream        => 1,     qw/task run/) }
270sub use_timeout       { $_[0]->{+USE_TIMEOUT}       //= $_[0]->_fallback(use_timeout       => 1,     qw/task/) }
271sub retry             { $_[0]->{+RETRY}             //= $_[0]->_fallback(retry             => undef, qw/task run/) }
272sub event_timeout     { $_[0]->{+EVENT_TIMEOUT}     //= $_[0]->_fallback(event_timeout     => undef, qw/task runner/) }
273sub post_exit_timeout { $_[0]->{+POST_EXIT_TIMEOUT} //= $_[0]->_fallback(post_exit_timeout => undef, qw/task runner/) }
274
275
276sub args { @{$_[0]->{+ARGS} //= $_[0]->_merge_sources(test_args => qw/task run/)} }
277sub load { @{$_[0]->{+LOAD} //= [@{$_[0]->run->load // []}]} }
278
279sub cli_includes {
280    my $self = shift;
281
282    # '.' is handled via the PERL_USE_UNSAFE_INC env var set later
283    $self->{+CLI_INCLUDES} //= [map { "-I$_" } grep { $_ ne '.' } $self->includes];
284
285    return @{$self->{+CLI_INCLUDES}};
286}
287
288sub runner_includes { @{$_[0]->{+RUNNER_INCLUDES} //= [$_[0]->{+RUNNER}->all_libs]} }
289
290sub _merge_sources {
291    my $self = shift;
292    my ($name, @from) = @_;
293
294    my @vals;
295    for my $from (@from) {
296        my $source = $self->$from;
297        my $val = blessed($source) ? $source->$name : $source->{$name};
298        next unless defined $val;
299        next unless @$val;
300        push @vals => @$val;
301    }
302
303    return \@vals;
304}
305
306sub _fallback {
307    my $self = shift;
308    my ($name, $default, @from) = @_;
309
310    my @vals;
311    for my $from (@from) {
312        my $source = $self->$from;
313        my $val = blessed($source) ? $source->$name : $source->{$name};
314        push @vals => $val if defined $val;
315    }
316
317    return $default unless @vals;
318
319    # If the default is a ref we will just return the first value we found, truthiness check is useless
320    return shift @vals if ref $default || !defined($default) || $default !~ m/^(0|1)$/;
321
322    # If the default is true, then we only return true if none of the vals are false
323    return !grep { !$_ } @vals if $default;
324
325    # If the default is false, then we return true if any of the valse are true
326    return grep { $_ } @vals;
327}
328
329sub job_dir {
330    my $self = shift;
331    return $self->{+JOB_DIR} if $self->{+JOB_DIR};
332
333    my $job_dir = File::Spec->catdir($self->run_dir, $self->{+TASK}->{job_id} . '+' . $self->is_try);
334    mkdir($job_dir) or die "$$ $0 Could not create job directory '$job_dir': $!";
335    chmod_tmp($job_dir);
336    $self->{+JOB_DIR} = $job_dir;
337}
338
339sub tmp_dir {
340    my $self = shift;
341
342    return $self->{+TMP_DIR} if $self->{+TMP_DIR};
343
344    my $tmp_dir = File::Temp::tempdir("XXXXXX", DIR => $self->runner->tmp_dir);
345    chmod_tmp($tmp_dir);
346
347    $self->{+TMP_DIR} = clean_path($tmp_dir);
348}
349
350sub make_event_dir { $_[0]->event_dir }
351sub event_dir {
352    my $self = shift;
353    return $self->{+EVENT_DIR} if $self->{+EVENT_DIR};
354
355    my $events_dir = File::Spec->catdir($self->job_dir, 'events');
356    unless (-d $events_dir) {
357        mkdir($events_dir) or die "$$ $0 Could not create events directory '$events_dir': $!";
358    }
359    $self->{+EVENT_DIR} = $events_dir;
360}
361
362sub in_file {
363    my $self = shift;
364    return $self->{+IN_FILE} if $self->{+IN_FILE};
365
366    my $task = $self->{+TASK};
367
368    unless ($task->{input}) {
369        my $from_run = $self->run->input_file;
370        return $self->{+IN_FILE} = $from_run if $from_run;
371    }
372
373    my $stdin = File::Spec->catfile($self->job_dir, 'stdin');
374
375    my $content = $task->{input} // $self->run->input // '';
376    write_file($stdin, $content);
377
378    return $self->{+IN_FILE} = $stdin;
379}
380
381sub use_fork {
382    my $self = shift;
383
384    return $self->{+USE_FORK} if defined $self->{+USE_FORK};
385
386    my $task = $self->{+TASK};
387
388    return $self->{+USE_FORK} = 0 unless CAN_REALLY_FORK;
389    return $self->{+USE_FORK} = 0 if $task->{binary};
390    return $self->{+USE_FORK} = 0 if $task->{non_perl};
391    return $self->{+USE_FORK} = 0 if defined($task->{use_fork}) && !$task->{use_fork};
392    return $self->{+USE_FORK} = 0 if defined($task->{use_preload}) && !$task->{use_preload};
393
394    # -w switch is ok, otherwise it is a no-go
395    return $self->{+USE_FORK} = 0 if grep { !m/\s*-w\s*/ } $self->switches;
396
397    my $runner = $self->{+RUNNER};
398    return $self->{+USE_FORK} = 0 unless $runner->use_fork;
399
400    return $self->{+USE_FORK} = 1;
401}
402
403sub includes {
404    my $self = shift;
405
406    return @{$self->{+INCLUDES}} if $self->{+INCLUDES};
407
408    $self->{+INCLUDES} = [
409        process_includes(
410            list            => [$self->runner_includes, @{$self->{+SETTINGS}->harness->orig_inc}],
411            include_dot     => $self->unsafe_inc,
412            include_current => 1,
413            clean           => 1,
414            $self->ch_dir ? (ch_dir => $self->ch_dir) : (),
415        )
416    ];
417
418    return @{$self->{+INCLUDES}};
419}
420
421sub cli_options {
422    my $self = shift;
423
424    my $event_dir = $self->event_dir;
425    my $job_id = $self->job_id;
426
427    return (
428        $self->use_stream  ? ("-MTest2::Formatter::Stream=dir,$event_dir,job_id,$job_id") : (),
429        $self->event_uuids ? ('-MTest2::Plugin::UUID')                     : (),
430        $self->mem_usage   ? ('-MTest2::Plugin::MemUsage')                 : (),
431        $self->io_events   ? ('-MTest2::Plugin::IOEvents')                 : (),
432        (map { @{$_->[1]} ? "-M$_->[0]=" . join(',' => @{$_->[1]}) : "-M$_->[0]" } $self->load_import),
433        (map { "-m$_" } $self->load),
434    );
435}
436
437sub switches {
438    my $self = shift;
439
440    return @{$self->{+SWITCHES}} if $self->{+SWITCHES};
441
442    my @switches;
443
444    my %seen;
445    for my $s (@{$self->{+TASK}->{switches} // []}) {
446        $seen{$s}++;
447        $self->{+USE_W_SWITCH} = 1 if $s =~ m/\s*-w\s*/;
448        push @switches => $s;
449    }
450
451    my %seen2;
452    for my $s (@{$self->{+RUNNER}->switches // []}) {
453        next if $seen{$s};
454        $seen2{$s}++;
455        $self->{+USE_W_SWITCH} = 1 if $s =~ m/\s*-w\s*/;
456        push @switches => $s;
457    }
458
459    for my $s ($self->switches_from_env) {
460        next if $seen{$s};
461        next if $seen2{$s};
462        $self->{+USE_W_SWITCH} = 1 if $s =~ m/\s*-w\s*/;
463        push @switches => $s;
464    }
465
466    return @{$self->{+SWITCHES} = \@switches};
467}
468
469sub prof_file {
470    my $self = shift;
471    my $file =$self->rel_file;
472
473    $file =~ s{/}{-}g;
474    $file =~ s{\.[^\.]+$}{.nytprof}g;
475
476    return $file;
477}
478
479sub env_vars {
480    my $self = shift;
481
482    return $self->{+ENV_VARS} if $self->{+ENV_VARS};
483
484    my $from_run = $self->run->env_vars;
485    my $from_task = $self->{+TASK}->{env_vars};
486
487    my @p5l = ($from_task->{PERL5LIB}, $from_run->{PERL5LIB});
488    push @p5l => $self->includes if $self->{+TASK}->{binary} || $self->{+TASK}->{non_perl};
489    push @p5l => $ENV{PERL5LIB} if $ENV{PERL5LIB};
490    my $p5l = join $Config{path_sep} => grep { defined $_ && $_ ne '.' } @p5l;
491
492    my $verbose = $self->verbose;
493
494    return $self->{+ENV_VARS} = {
495        $from_run  ? (%$from_run)  : (),
496        $from_task ? (%$from_task) : (),
497
498        $self->use_stream ? (T2_FORMATTER => 'Stream', T2_STREAM_DIR => $self->event_dir, T2_STREAM_JOB_ID => $self->job_id) : (),
499
500        $self->{+SETTINGS}->runner->nytprof ? (NYTPROF => "addpid=1:start=begin") : (),
501
502        PERL5LIB            => $p5l,
503        PERL_USE_UNSAFE_INC => $self->unsafe_inc,
504        TEST2_JOB_DIR       => $self->job_dir,
505        TEST2_RUN_DIR       => $self->run_dir,
506        TMPDIR              => $self->tmp_dir,
507        TEMPDIR             => $self->tmp_dir,
508        SYSTEM_TMPDIR       => $self->{+SETTINGS}->harness->orig_tmp,
509        SYSTEM_TMPDIR_PERMS => $self->{+SETTINGS}->harness->orig_tmp_perms,
510
511        HARNESS_IS_VERBOSE    => $verbose,
512        T2_HARNESS_IS_VERBOSE => $verbose,
513
514        HARNESS_ACTIVE       => 1,
515        TEST2_HARNESS_ACTIVE => 1,
516
517        T2_HARNESS_JOB_FILE     => $self->rel_file,
518        T2_HARNESS_JOB_NAME     => $self->{+TASK}->{job_name},
519        T2_HARNESS_JOB_IS_TRY   => $self->{+IS_TRY}           // 0,
520        T2_HARNESS_JOB_DURATION => $self->{+TASK}->{duration} // '',
521    };
522}
523
524sub load_import {
525    my $self = shift;
526
527    return @{$self->{+LOAD_IMPORT}} if $self->{+LOAD_IMPORT};
528
529    my $from_run = $self->run->load_import;
530
531    my @out;
532    for my $mod (@{$from_run->{'@'} // []}) {
533        push @out => [$mod, $from_run->{$mod} // []];
534    }
535
536    return @{$self->{+LOAD_IMPORT} = \@out};
537}
538
539sub use_w_switch {
540    my $self = shift;
541    return $self->{+USE_W_SWITCH} if defined $self->{+USE_W_SWITCH};
542    $self->switches;
543    return $self->{+USE_W_SWITCH};
544}
545
546sub set_exit {
547    my $self = shift;
548    my ($runner, $exit, $time, @args) = @_;
549
550    $self->SUPER::set_exit(@_);
551
552    my $file = File::Spec->catfile($self->job_dir, 'exit');
553
554    my $e = parse_exit($exit);
555
556    write_file_atomic($file, join(" " => $exit, $e->{err}, $e->{sig}, $e->{dmp}, $time, @args));
557}
558
5591;
560
561__END__
562
563=pod
564
565=encoding UTF-8
566
567=head1 NAME
568
569Test2::Harness::Runner::Job - Representation of a test job.
570
571=head1 DESCRIPTION
572
573This module takes all the data from a test file queue item, a run, and runner
574settings, and mashes them together to figure out what is actually needed to run
575a job.
576
577=head1 METHODS
578
579Note, this object subclasses L<Test2::Harness::IPC::Process>.
580
581=over 4
582
583=item $arrayref = $job->args
584
585Get the arguments for the test either formt he queue item, or from the run.
586
587=item $path = $job->bail_file
588
589Path to the events-file used in case of a bail-out
590
591=item $bool = $job->bailed_out
592
593True if the test job bailed out.
594
595=item $cat $job->category
596
597Process category, always 'job' unless overriden in a subclass.
598
599=item $path = $job->ch_dir
600
601If this job first requires a change in directory before running, this will
602return the path.
603
604=item @list = $job->cli_includes
605
606List of includes for a command line launch of this job.
607
608=item @list = $job->cli_options
609
610List of options for a command line launch of this job.
611
612=item $hashref = $job->env_vars
613
614Get environment variables to set when launching this job.
615
616=item $path = $job->out_file
617
618File to which all STDOUT for the job will be written.
619
620=item $path = $job->err_file
621
622File to which all STDERR for the job will be written.
623
624=item $path = $job->et_file
625
626File to which event timeout notifications will be written.
627
628=item $path = $job->pet_file
629
630File to which post exit timeout events will be written.
631
632=item $path = $job->event_dir
633
634Directory to which L<Test2::Formatter::Stream> events will be written.
635
636=item $time = $job->event_timeout
637
638Event timeout specification, if any, first from test queue item, then from
639runner.
640
641=item $time = $job->post_exit_timeout
642
643Post exit timeout specification, if any, first from test queue item, then from
644runner.
645
646=item $bool = $job->event_uuids
647
648Use L<Test2::Plugin::UUID> inside the test.
649
650=item $path = $job->file
651
652Test file the job will be running.
653
654=item $coderef = $job->fork_callback
655
656If the job is to be launched via fork, use this callback.
657
658=item $path = $job->in_file
659
660File containing STDIN to be provided to the test.
661
662=item @list = $job->includes
663
664Paths to add to @INC for the test.
665
666=item $bool = $job->io_events
667
668True if L<Test2::Plugin::IOEvents> should be used.
669
670=item $int = $job->is_try
671
672This starts at 0 and will be incremented for every retry of the job.
673
674=item $path = $job->job_dir
675
676Temporary directory housing all files related to this job when it runs.
677
678=item $uuid = $job->job_id
679
680UUID for this job.
681
682=item @list = $job->load
683
684Modules to load when starting this job.
685
686=item @list = $job->load_import
687
688Modules to load and import when starting this job.
689
690=item $bool = $job->mem_usage
691
692True if the L<Test2::Plugin::MemUsage> plugin should be used.
693
694=item $path = $job->run_file
695
696Usually the same as rel_file, but you can specify an alternative file to
697actually run.
698
699=item $path = $job->rel_file
700
701Relative path to the file.
702
703=item $int = $job->retry
704
705How many times the test should be retried if it fails.
706
707=item $bool = $job->retry_isolated
708
709True if the test should be retried in isolation if it fails.
710
711=item $run = $job->run
712
713The L<Test2::Harness::Runner::Run> instance.
714
715=item $path = $job->run_dir
716
717Path to the temporary directory housing all the data about the run.
718
719=item $runner = $job->runner
720
721The L<Test2::Harness::Runner> instance.
722
723=item @list = $job->runner_includes
724
725Search path includes provided directly by the runner.
726
727=item $settings = $job->settings
728
729The L<Test2::Harness::Settings> instance.
730
731=item $bool = $job->smoke
732
733True if the test is a priority smoke test.
734
735=item $hashref = $job->spawn_params
736
737Parameters for C<run_cmd()> in L<Test2::Harness::Util::IPC> when launching this
738job.
739
740=item @list = $job->switches
741
742Command line switches for perl when running this test.
743
744=item $hashref = $job->task
745
746Task data from the queue.
747
748=item $path = $job->tmp_dir
749
750Temp dir created specifically for this job.
751
752=item $bool = $job->unsafe_inc
753
754True if '.' should be added to C<@INC>.
755
756=item $bool = $job->use_fork
757
758True if this job should be launched via fork.
759
760=item $bool = $job->use_stream
761
762True if this job should use L<Test2::Formatter::Stream>.
763
764=item $bool = $job->use_timeout
765
766True if this job should timeout due to lack of activity.
767
768=item $bool = $job->use_w_switch
769
770True if the C<-w> switch should be used for this test.
771
772=back
773
774=head1 SOURCE
775
776The source code repository for Test2-Harness can be found at
777F<http://github.com/Test-More/Test2-Harness/>.
778
779=head1 MAINTAINERS
780
781=over 4
782
783=item Chad Granum E<lt>exodist@cpan.orgE<gt>
784
785=back
786
787=head1 AUTHORS
788
789=over 4
790
791=item Chad Granum E<lt>exodist@cpan.orgE<gt>
792
793=back
794
795=head1 COPYRIGHT
796
797Copyright 2020 Chad Granum E<lt>exodist7@gmail.comE<gt>.
798
799This program is free software; you can redistribute it and/or
800modify it under the same terms as Perl itself.
801
802See F<http://dev.perl.org/licenses/>
803
804=cut
805