Clustericious

 view release on metacpan or  search on metacpan

lib/Test/Clustericious/Command.pm  view on Meta::CPAN

package Test::Clustericious::Command;

use strict;
use warnings;
use 5.010001;
use Test2::Plugin::FauxHomeDir;
use File::Glob qw( bsd_glob );
use base qw( Exporter );
use Exporter qw( import );
use Mojo::Loader;
use Path::Class qw( file dir );
use Env qw( @PERL5LIB @PATH );
use Capture::Tiny qw( capture );
use File::Which qw( which );
use File::Glob qw( bsd_glob );
use YAML::XS ();
use File::Temp qw( tempdir );
use Test2::API qw( context );

# ABSTRACT: Test Clustericious commands
our $VERSION = '1.29'; # VERSION


our @EXPORT      = qw( extract_data mirror requires run_ok generate_port note_file clean_file create_symlink );
our @EXPORT_OK   = @EXPORT;
our %EXPORT_TAGS = ( all => \@EXPORT );

unshift @INC, dir(bsd_glob '~/lib')->stringify;
unshift @PERL5LIB, map { dir($_)->absolute->stringify } @INC;
unshift @PATH, dir(bsd_glob '~/bin')->stringify;

sub _can_execute_in_tmp
{
  my $script = file( tempdir( CLEANUP => 1 ), 'mytest' );
  $script->spew("#!$^X\nexit 0");
  chmod 0755, "$script";
  my $exit;
  capture { system "$script", "okay"; $exit = $? };
  $exit == 0;
}

sub requires
{
  my($command, $num) = @_;

  my $ctx = context();
  $ctx->plan( 0, 'SKIP', 'test requires execute in tmp') unless __PACKAGE__->_can_execute_in_tmp;

  unless(defined $command)
  {
    $ctx->plan( $num ) if defined $num;
    $ctx->release;
    return;
  }

  if($command =~ /^(.*)\.conf$/)
  {
    my $name = $1;
    if(defined $ENV{CLUSTERICIOUS_COMMAND_TEST} && -r $ENV{CLUSTERICIOUS_COMMAND_TEST})
    {
      my $config = do {
        require Clustericious::Config;
        my $config = Clustericious::Config->new($ENV{CLUSTERICIOUS_COMMAND_TEST});
        my %config = %$config;
        \%config;
      }->{$name};
      $ctx->plan( 0, 'SKIP', "developer test not configured" ) unless defined $config;
      
      unshift @PATH, $config->{path} if defined $config->{path};
      unshift @PATH, dir(bsd_glob '~/bin')->stringify;
      $ENV{$_} = $config->{env}->{$_} for keys %{ $config->{env} };
      $command = $config->{exe} // $name;
    }
    else
    {
      $ctx->plan( 0, 'SKIP', "developer only test" );
    }
  }

  if(which $command)
  {
    $ctx->plan( $num ) if defined $num;
  }
  else
  {
    $ctx->plan( 0, 'SKIP', "test requires $command to be in the PATH" );
  }
  $ctx->release;
}

sub extract_data
{
  my(@values) = @_;
  my $caller = caller;
  Mojo::Loader::load_class($caller) unless $caller eq 'main';
  my $all = Mojo::Loader::data_section $caller;
  
  my $ctx = context();
  
  foreach my $name (sort keys %$all)
  {
    my $file = file(bsd_glob('~'), $name);
    my $dir  = $file->parent;
    unless(-d $dir)
    {
      $ctx->note("[extract] DIR  $dir");
      $dir->mkpath(0,0700);
    }
    unless(-f $file)
    {
      $ctx->note("[extract] FILE $file@{[ $name =~ m{^bin/} ? ' (*)' : '']}");
      
      if($name =~ m{^bin/})
      {
        my $content = $all->{$name};
        $content =~ s{^#!/usr/bin/perl}{#!$^X};
        $file->spew($content);
        chmod 0700, "$file";
      }
      else
      {
        $file->spew($all->{$name});
      }
    }
  }
  
  $ctx->release;
}

sub mirror
{
  my($src, $dst) = map { ref($_) ? $_ : dir($_) } @_;
  
  my $ctx = context();

  $dst = dir(bsd_glob('~'), $dst) unless $dst->is_absolute;
  
  unless(-d $dst)
  {
    $ctx->note("[mirror ] DIR  $dst");
    $dst->mkpath(0,0700);
  }
  
  foreach my $child ($src->children)
  {
    if($child->is_dir)
    {
      mirror($child, $dst->subdir($child->basename));
    }
    else
    {
      my $dst = $dst->file($child->basename);
      unless(-f $dst)
      {
        if(-x $child)
        {
          $ctx->note("[mirror ] FILE $dst (*)");
          my $content = scalar $child->slurp;
          $content =~ s{^#!/usr/bin/perl}{#!$^X};
          $dst->spew($content);
          chmod 0700, "$dst";
        }
        else
        {
          $ctx->note("[mirror ] FILE $dst");
          $dst->spew(scalar $child->slurp);
          chmod 0600, "$dst";
        }
      }
    }
  }
  
  $ctx->release;
}

sub run_ok
{
  my(@cmd) = @_;
  
  # Yath set some environment variables which confuse a subprocess
  # for when we are testing the use of prove, etc
  local %ENV = %ENV;
  delete $ENV{$_} for grep /^T2_/, keys %ENV;
  
  my($out, $err, $error, $exit) = capture { system @cmd; ($!,$?) };
  
  my $ok = ($exit != -1) && ! ($exit & 128);
  
  my $ctx = context();
  
  $ctx->ok($ok, "run: @cmd");
  $ctx->diag("  @cmd failed") unless $ok;
  $ctx->diag("    - execute failed: $error") if $exit == -1;
  $ctx->diag("    - died from signal: " . ($exit & 128)) if $exit & 128;

  my $run = Test::Clustericious::Command::Run->new(
    cmd => \@cmd,
    out => $out, err => $err, exit => $exit >> 8,
  );
  
  $ctx->release;
  
  $run;
}

sub generate_port
{
  require IO::Socket::INET;
  IO::Socket::INET->new(Listen => 5, LocalAddr => "127.0.0.1")->sockport;
}

sub note_file
{
  my $ctx = context();
  foreach my $file (sort map { file $_ } map { bsd_glob "~/$_" } @_)
  {
    $ctx->note("[content] $file");
    $ctx->note(scalar $file->slurp);
  }
  $ctx->release;
}

sub clean_file
{
  foreach my $file (sort map { file $_ } map { bsd_glob "~/$_" } @_)
  {
    $file->remove;



( run in 1.679 second using v1.01-cache-2.11-cpan-39bf76dae61 )