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