![Automated Tests](https://github.com/bodo-hugo-barwich/Process/workflows/Automated%20Tests/badge.svg)
[![Build Status](https://travis-ci.com/bodo-hugo-barwich/Process.svg?branch=master)](https://travis-ci.com/bodo-hugo-barwich/Process)

# Process
Process::SubProcess - Perl Module for Multiprocessing

Running Sub Processes in an easy way while reading STDOUT, STDERR, Exit Code and possible System Errors. \
It also implements running multiple Sub Processes simultaneously while keeping all Report and Error Messages and Exit Codes
seperate.

# Features
Some important Features are:
* Asynchronous Launch
* Reads Big Outputs
* Execution Timeout
* Configurable Read Interval
* Captures possible System Errors at Launch Time like "file not found" Errors

# Motivation
This Module was conceived out of the need to launch multiple Tasks simulaneously while still keeping each Log and Error Messages and Exit Codes separately. \
As I developed it as Prototype at:
[Multi Process Manager](https://stackoverflow.com/questions/50177534/why-do-pipes-from-child-processes-break-sometimes-and-sometimes-not)\
The **Object Oriented Design** permits the implementation of the **[Command Pattern / Manager-Worker Pattern](https://en.wikipedia.org/wiki/Command_pattern)** with the `Process::SubProcess::Group` and `Process::SubProcess::Pool` Packages.\
Having a similar implementation as the [`Capture::Tiny` Package](https://metacpan.org/pod/Capture::Tiny) it eventually evolved as a Procedural Replacement for the `Capture::Tiny::capture()` Function.

## Example Use Case
The Usefulness of this Library is best shown by an Example Use Case as seen in the `Process::SubProcess::Group::Run` Test Sequence:\
Having 3 Jobs at hand of 2 seconds, 3 seconds and 1 second running them sequencially would take aproximately **6 seconds**.\
But using the `Process::SubProcess::Group` it takes effectively only **3 seconds** to complete.\
And still each Job can be evaluated separately by their own Results keeping Log Message separate from Error Messages and viewing them in their context.
```
# Subtest: Process::SubProcess::Group::Run
    ok 1 - scripts (count: '3'): added correctly
Process Group Execution Start - Time Now: '1688542787.31262' s
    ok 2 - Process Group Execution: Execution correct
Process Group Execution End - Time Now: '1688542790.33528' s
Process Group Execution finished in '3022.66407012939' ms
    ok 3 - Process No. '0': Listed correctly
Process (8608) 'test-script:2s':
ERROR CODE: '0'
EXIT CODE: '0'
STDOUT: 'Start - Time Now: '1688542787.33535' s
script 'test_script.pl' START 0
script 'test_script.pl' PAUSE '2' ...
script 'test_script.pl' END 1
End - Time Now: '1688542789.33552' s
script 'test_script.pl' done in '2000.16403198242' ms
script 'test_script.pl' EXIT '0'
'
STDERR: 'script 'test_script.pl' START 0 ERROR
script 'test_script.pl' END 1 ERROR
'
    ok 4 - Process No. '1': Listed correctly
Process (8609) 'test-script:3s':
ERROR CODE: '0'
EXIT CODE: '0'
STDOUT: 'Start - Time Now: '1688542787.3336' s
script 'test_script.pl' START 0
script 'test_script.pl' PAUSE '3' ...
script 'test_script.pl' END 1
End - Time Now: '1688542790.3338' s
script 'test_script.pl' done in '3000.19979476929' ms
script 'test_script.pl' EXIT '0'
'
STDERR: 'script 'test_script.pl' START 0 ERROR
script 'test_script.pl' END 1 ERROR
'
    ok 5 - Process No. '2': Listed correctly
Process (8610) 'test-script:1s':
ERROR CODE: '0'
EXIT CODE: '0'
STDOUT: 'Start - Time Now: '1688542787.34636' s
script 'test_script.pl' START 0
script 'test_script.pl' PAUSE '1' ...
script 'test_script.pl' END 1
End - Time Now: '1688542788.34656' s
script 'test_script.pl' done in '1000.20098686218' ms
script 'test_script.pl' EXIT '0'
'
STDERR: 'script 'test_script.pl' START 0 ERROR
script 'test_script.pl' END 1 ERROR
'
    1..5
ok 3 - Process::SubProcess::Group::Run
```

# Usage
## runSubProcess() Function
Demonstrating the `runSubProcess()` Function Use Case:
```perl
use Process::SubProcess qw(runSubProcess);

use Test::More;


my $spath = '/path/to/test/script/';
my $stestscript = 'test_script.pl';
my $itestpause = 3;
my $iteststatus = 4;

my $rscriptlog = undef;
my $rscripterror = undef;
my $iscriptstatus = -1;
my $irunok = -1;


subtest 'runSubProcess() Function' => sub {

  #Execute the Command
  ($rscriptlog, $rscripterror, $iscriptstatus)
    = runSubProcess("${spath}${stestscript} $itestpause $iteststatus");

  #Evaluate the Results

  isnt($rscriptlog, undef, "STDOUT Ref is returned");
  isnt($rscripterror, undef, "STDERR Ref is returned");
  isnt($iscriptstatus, undef, "EXIT CODE is returned");
  ok($iscriptstatus =~ qr/^-?\d$/, "EXIT CODE is numeric");
  is($iscriptstatus, $iteststatus, 'EXIT CODE is correct');

  print("EXIT CODE: '$iscriptstatus'\n");

  if(defined $rscriptlog)
  {
    isnt($$rscriptlog, '', "STDOUT was captured");

    print("STDOUT: '$$rscriptlog'\n");
  } #if(defined $rscriptlog)

  if(defined $rscripterror)
  {
    isnt($$rscripterror, '', "STDERR was captured");

    print("STDERR: '$$rscripterror'\n");
  } #if(defined $rscripterror)
};

done_testing();
```

# Documentation
The Class Diagramm kann be found at:\
[Class Diagram for the Package 'Process'](docs/Process.jpg)\
![Class Diagram for the Package 'Process'](docs/Process.jpg)