class TestCase::Sys::Process {
  use Sys::Process;
  use Sys::Process::Constant as PROCESS;
  use Sys::Time;
  use Sys;
  use Sys::IO::Constant as IO;
  
  static method fork : int () {
    
    {
      my $child_process_id = Sys::Process->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
    }
    
    {
      my $child_process_id = Sys->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
    }
    
    return 1;
  }

  static method getpriority : int () {
    
    {
      my $nice = Sys::Process->getpriority(PROCESS->PRIO_PROCESS, 0);
      
      unless ($nice >= -20 && $nice <= 19) {
        return 0;
      }
    }
    
    {
      my $nice = Sys->getpriority(PROCESS->PRIO_PROCESS, 0);
      
      unless ($nice >= -20 && $nice <= 19) {
        return 0;
      }
    }
    
    # The constant values
    {
      PROCESS->PRIO_PROCESS;
      PROCESS->PRIO_PGRP;
      PROCESS->PRIO_USER;
    }
    
    return 1;
  }

  static method setpriority : int () {
    
    my $not_supported = 0;
    my $failed = 0;
    {
      my $status = -1;
      eval { $status = Sys::Process->setpriority(PROCESS->PRIO_PROCESS, 0, 0); };
      
      if ($@) {
        if (eval_error_id == basic_type_id Error::NotSupported) {
          $not_supported = 1;
        }
        elsif (eval_error_id == basic_type_id Error::System) {
          $failed = 1;
        }
        else {
          die $@;
        }
      }
    }
    
    if ($not_supported) {
      warn "[Test Skip]setpriority is not supported in this system.";
    }
    elsif ($failed) {
      warn "[Test Skip]setpriority failed in this system.";
    }
    else {
      Sys->setpriority(PROCESS->PRIO_PROCESS, 0, 0);
    }
    
    return 1;
  }
  
  static method sleep : int () {
    
    my $start = Sys::Time->time;
    
    my $rest = Sys::Process->sleep(1);
    
    unless ($rest == 0) {
      return 0;
    }
    
    unless (Sys::Time->time - $start >= 1) {
      return 0;
    }
    
    return 1;
  }

  static method wait : int () {
    
    {
      my $child_process_id = Sys::Process->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
      
      my $status = -1;
      my $ret_child_process_id = Sys::Process->wait(\$status);
      
      unless ($status == 0) {
        return 0;
      }
      unless ($ret_child_process_id > 0) {
        return 0;
      }
    }
    
    {
      my $child_process_id = Sys::Process->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
      
      my $status = -1;
      my $ret_child_process_id = Sys->wait(\$status);
      
      unless ($status == 0) {
        return 0;
      }
      unless ($ret_child_process_id > 0) {
        return 0;
      }
    }
    
    return 1;
  }

  static method waitpid : int () {
    
    {
      my $child_process_id = Sys::Process->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
      
      my $status = -1;
      
      my $ret_child_process_id = -1;
      while (1) {
        $ret_child_process_id = Sys::Process->waitpid(-1, \$status, PROCESS->WNOHANG);
        
        if (Sys::Process->WIFEXITED($status)) {
          last;
        }
      }
      
      unless ($ret_child_process_id >= 0) {
        return 0;
      }
    }
    
    {
      my $child_process_id = Sys::Process->fork;
      
      # The child process
      if ($child_process_id == 0) {
        Sys::Process->exit(0);
      }
      
      unless ($child_process_id > 0) {
        return 0;
      }
      
      my $status = -1;
      
      my $ret_child_process_id = -1;
      while (1) {
        $ret_child_process_id = Sys->waitpid(-1, PROCESS->WNOHANG, \$status);
        
        if (Sys::Process->WIFEXITED($status)) {
          last;
        }
      }
      
      unless ($ret_child_process_id >= 0) {
        return 0;
      }
    }
    
    # The status cheking methods
    {
      Sys::Process->WIFEXITED(0);
      Sys::Process->WEXITSTATUS(0);
      Sys::Process->WIFSIGNALED(0);
      Sys::Process->WTERMSIG(0);
      Sys::Process->WCOREDUMP(0);
      Sys::Process->WIFSTOPPED(0);
      Sys::Process->WSTOPSIG(0);
      Sys::Process->WIFCONTINUED(0);
    }
    
    return 1;
  }

  static method system : int () {
    
    {
      my $cmd = "perl -e \"warn qq(\\n[Test Output]system);\"";
      my $status = Sys::Process->system($cmd);
      
      unless ($status == 0) {
        return 0;
      }
    }
    
    {
      my $cmd = "perl -e \"warn qq(\\n[Test Output]system);\"";
      my $status = Sys->system($cmd);
      
      unless ($status == 0) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method exit_success : void () {
    
    Sys::Process->exit(PROCESS->EXIT_SUCCESS);
  }
  
  static method exit_failure : void () {
    
    Sys::Process->exit(PROCESS->EXIT_FAILURE);
  }
  
  static method sys_exit_success : void () {
    
    Sys->exit(PROCESS->EXIT_SUCCESS);
  }
  
  static method pipe : int () {
    
    {
      if (Sys::OS->is_windows) {
         my $fds = new int [2];
        my $status = Sys::Process->_pipe($fds, 0, IO->O_BINARY);
        
        unless ($status == 0) {
          return 0;
        }
        
        unless ($fds->[0] > 0) {
          return 0;
        }
        
        unless ($fds->[1] > 0) {
          return 0;
        }
     }
      else {
        my $fds = new int [2];
        my $status = Sys::Process->pipe($fds);
        
        unless ($status == 0) {
          return 0;
        }
        
        unless ($fds->[0] > 0) {
          return 0;
        }
        
        unless ($fds->[1] > 0) {
          return 0;
        }
      }
    }
    
    {
      my $write_fd = -1;
      my $read_fd = -1;
      Sys->pipe(\$write_fd, \$read_fd);
      
      unless ($write_fd > 0) {
        return 0;
      }
      
      unless ($read_fd > 0) {
        return 0;
      }
      
      unless (Sys::OS->is_windows) {
        my $flag0 = Sys->fcntl($write_fd, IO->F_GETFD);
        
        if ($flag0 & IO->FD_CLOEXEC) {
          return 0;
        }
        
        my $flag1 = Sys->fcntl($read_fd, IO->F_GETFD);
        
        if ($flag1 & IO->FD_CLOEXEC) {
          return 0;
        }
      }
    }
    
    return 1;
  }
  
  static method getpgid : int () {
    
    {
      my $process_group = Sys::Process->getpgid(0);
      
      unless ($process_group >= 0) {
        return 0;
      }
    }
    
    {
      my $process_group = Sys->getpgrp(0);
      
      unless ($process_group >= 0) {
        return 0;
      }
    }
    
    return 1;
  }

  static method setpgid : int () {
    
    {
      my $status = Sys::Process->setpgid(0, 0);
      
      unless ($status == 0) {
        return 0;
      }
    }
    
    {
      Sys->setpgrp(0, 0);
    }
    
    return 1;
  }
  
  static method getpid : int () {
    
    my $process_id = Sys::Process->getpid;
    
    unless ($process_id >= 0) {
      return 0;
    }
    
    return 1;
  }
  
  static method getppid : int () {
    
    {
      my $process_id = Sys::Process->getppid;
      
      unless ($process_id >= 0) {
        return 0;
      }
    }
    
    {
      my $process_id = Sys->getppid;
      
      unless ($process_id >= 0) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method execv_success : int ($perl_path : string, $program_file : string) {
    my $args = [$perl_path, $program_file, "abc"];
    Sys::Process->execv($perl_path, $args);
  }
  
  static method exec_sys : int ($perl_path : string, $program_file : string) {
    my $args = [$program_file, "abc"];
    Sys->exec($perl_path, $args);
  }
  
  static method usleep : int () {
    
    my $start = Sys::Time->time;
    
    my $status = Sys::Process->usleep(1_100_000);
    
    unless ($status  == 0) {
      return 0;
    }
    
    unless (Sys::Time->time - $start >= 1) {
      return 0;
    }
    
    return 1;
  }
  
  static method setsid : int () {
    
    my $child_pid = Sys::Process->fork;
    
    # Parent
    if ($child_pid) {
      # Do nothing
    }
    # Child
    else {
      my $session_id = Sys::Process->setsid;
      
      diag "[Test Output]setsid:$session_id";
      
      Sys::Process->exit(0);
    }
    
    return 1;
  }
  
}