<commit_before><?php
$_SERVER['migrate_debug'] = 0;

# IMPROVE: Write assert_template().
# IMPROVE: Write assert_dom_equal() & assert_dom_not_equal().
# IMPROVE: Write assert_tag() & assert_not_tag().
class Unit_FunctionalTest extends Unit_TestCase
{
  protected $last_action;
  
  # IMPROVE: Start and stop a test server (script/server -p 3009 -e test).
  function __construct()
  {
    $map = ActionController_Routing::draw();
    $map->build_path_and_url_helpers();
    
    parent::__construct();
  }
  
  protected function assert_redirected_to($comment, $url)
  {
    $location = isset($this->last_action['headers']['location']) ?
      $this->last_action['headers']['location'] : false;
    $this->assert_equal($comment, $location, ($url === false) ? false : (string)$url);
  }
  
  protected function assert_response($comment, $status)
  {
    $this->assert_equal($comment, $this->last_action['status'], $status);
  }
  
  protected function assert_cookie_presence($comment, $cookie)
  {
    $this->assert_true($comment, isset($this->last_action['headers']['cookies'][$cookie]));
  }
  
  protected function assert_cookie_not_present($comment, $cookie)
  {
    $this->assert_false($comment, isset($this->last_action['headers']['cookies'][$cookie]));
  }
  
  protected function assert_cookie_equal($comment, $cookie, $expected)
  {
    $value = isset($this->last_action['headers']['cookies'][$cookie]) ?
      $this->last_action['headers']['cookies'][$cookie] : null;
    $this->assert_equal($comment, $value, $expected);
  }
  
  protected function assert_cookie_not_equal($comment, $cookie, $expected)
  {
    $value = isset($this->last_action['headers']['cookies'][$cookie]) ?
      $this->last_action['headers']['cookies'][$cookie] : null;
    $this->assert_not_equal($comment, $value, $expected);
  }
  
  # Executes an action on test server.
  # 
  # Examples:
  # 
  #   $this->run_action('GET', '/accounts');
  #   $this->run_action('PUT', '/accounts', array('account' => array('user_name' => 'azeroth'));
  #   
  #   $this->run_action(new_account_path());
  #   $this->run_action(delete_account_path(), array('account' => array('user_name' => 'azeroth'));
  # 
  protected function & run_action($method, $uri=null, $postfields=null, $files=null)
  {
    $args = func_get_args();
    if (is_object($args[0]))
    {
      $uri        = (string)$args[0];
      $method     = $args[0]->method;
      $postfields = isset($args[1]) ? $args[1] : null;
    }
    
    # requests a page
    $ch = curl_init();
    
    if (strpos($uri, '://') === false) {
      curl_setopt($ch, CURLOPT_URL, "http://localhost:3009$uri");
    }
    else {
      curl_setopt($ch, CURLOPT_URL, $uri);
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, "cURL");
    
    switch($method)
    {
      case 'GET':
        curl_setopt($ch, CURLOPT_HTTPGET, true);
      break;
      
      case 'PUT':
        $method = 'POST';
        $postfields['_method'] = 'PUT';
      
      case 'POST':
        curl_setopt($ch, CURLOPT_POST, true);
        if (!empty($postfields)) {
          curl_setopt($ch, CURLOPT_POSTFIELDS, $this->flatten_postfields($postfields));
        }
      break;
      /*
      case 'PUT':
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        if (!empty($postfields))
        {
          list($boundary, $data) = $this->build_multipart_form_data($postfields);
          curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data; boundary=$boundary"));
          curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
          print_r($data);
        }
      break;
      */
      case 'DELETE':
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
      break;
    }
    
    # executes the request
    $output = curl_exec($ch);
    
    if ($output === false)
    {
      echo "\n".Terminal::colorize('cURL error:', 'LIGHT_RED').' '.Terminal::colorize(curl_error($ch), 'RED ')."\n\n";
      die("Maybe you didn't started a test server?\n\$ script/server -e test -p 3009\n\n");
    }
    
    # parses headers
    $headers = array('cookies' => array());
    foreach(explode("\n", trim(substr($output, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE)))) as $line)
    {
      if (strpos($line, ':'))
      {
        list($header, $value) = explode(':', trim($line), 2);
        $header = strtolower($header);
        if ($header == 'set-cookie')
        {
          preg_match('/^\s*([^=]+)=([^;]+)/', $value, $match);
          $headers['cookies'][$match[1]] = $match[2];
        }
        $headers[$header] = trim($value);
      }
    }
    
    # gets additional informations
    $this->last_action = array(
      'url'        => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
      'method'     => $method,
      'postfields' => $postfields,
      'status'     => curl_getinfo($ch, CURLINFO_HTTP_CODE),
      'headers'    => $headers,
      'body'       => trim(substr($output, curl_getinfo($ch, CURLINFO_HEADER_SIZE))),
    );
    
    curl_close($ch);
    return $this->last_action;
  }
  
  private function & flatten_postfields(&$postfields)
  {
    $data = array();
    foreach(explode('&', http_build_query($postfields)) as $v)
    {
      list($k, $v) = explode('=', $v, 2);
      $data[urldecode($k)] = urldecode($v);
    }
    return $data;
  }
  /*
  private function build_multipart_form_data(&$postfields)
  {
    $data = "";
    $boundary = "---------------------".substr(md5(rand(0,32000)), 0, 10);
    
    foreach($postfields as $k => $v)
    {
      if (strpos(ltrim($v), '@') === 0)
      {
        $data .= "Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"\n"; 
        $data .= "Content-Type: ".mime_content_type($v)."\n"; 
        $data .= "Content-Transfer-Encoding: binary\n\n"; 
        $data .= file_get_contents($v)."\n"; 
        $data .= "--$boundary--\n"; 
      }<commit_msg>Fixed a bug in parsing postfields for FunctionalTest.<commit_after><?php
$_SERVER['migrate_debug'] = 0;

# IMPROVE: Write assert_template().
# IMPROVE: Write assert_dom_equal() & assert_dom_not_equal().
# IMPROVE: Write assert_tag() & assert_not_tag().
class Unit_FunctionalTest extends Unit_TestCase
{
  protected $last_action;
  
  # IMPROVE: Start and stop a test server (script/server -p 3009 -e test).
  function __construct()
  {
    $map = ActionController_Routing::draw();
    $map->build_path_and_url_helpers();
    
    parent::__construct();
  }
  
  protected function assert_redirected_to($comment, $url)
  {
    $location = isset($this->last_action['headers']['location']) ?
      $this->last_action['headers']['location'] : false;
    $this->assert_equal($comment, $location, ($url === false) ? false : (string)$url);
  }
  
  protected function assert_response($comment, $status)
  {
    $this->assert_equal($comment, $this->last_action['status'], $status);
  }
  
  protected function assert_cookie_presence($comment, $cookie)
  {
    $this->assert_true($comment, isset($this->last_action['headers']['cookies'][$cookie]));
  }
  
  protected function assert_cookie_not_present($comment, $cookie)
  {
    $this->assert_false($comment, isset($this->last_action['headers']['cookies'][$cookie]));
  }
  
  protected function assert_cookie_equal($comment, $cookie, $expected)
  {
    $value = isset($this->last_action['headers']['cookies'][$cookie]) ?
      $this->last_action['headers']['cookies'][$cookie] : null;
    $this->assert_equal($comment, $value, $expected);
  }
  
  protected function assert_cookie_not_equal($comment, $cookie, $expected)
  {
    $value = isset($this->last_action['headers']['cookies'][$cookie]) ?
      $this->last_action['headers']['cookies'][$cookie] : null;
    $this->assert_not_equal($comment, $value, $expected);
  }
  
  # Executes an action on test server.
  # 
  # Examples:
  # 
  #   $this->run_action('GET', '/accounts');
  #   $this->run_action('PUT', '/accounts', array('account' => array('user_name' => 'azeroth'));
  #   
  #   $this->run_action(new_account_path());
  #   $this->run_action(delete_account_path(), array('account' => array('user_name' => 'azeroth'));
  # 
  protected function & run_action($method, $uri=null, $postfields=null, $files=null)
  {
    $args = func_get_args();
    if (is_object($args[0]))
    {
      $uri        = (string)$args[0];
      $method     = $args[0]->method;
      $postfields = isset($args[1]) ? $args[1] : null;
    }
    
    # requests a page
    $ch = curl_init();
    
    if (strpos($uri, '://') === false) {
      curl_setopt($ch, CURLOPT_URL, "http://localhost:3009$uri");
    }
    else {
      curl_setopt($ch, CURLOPT_URL, $uri);
    }
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, "cURL");
    
    switch($method)
    {
      case 'GET':
        curl_setopt($ch, CURLOPT_HTTPGET, true);
      break;
      
      case 'PUT':
        $method = 'POST';
        $postfields['_method'] = 'PUT';
      
      case 'POST':
        curl_setopt($ch, CURLOPT_POST, true);
        if (!empty($postfields)) {
          curl_setopt($ch, CURLOPT_POSTFIELDS, $this->flatten_postfields($postfields));
        }
      break;
      /*
      case 'PUT':
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
        if (!empty($postfields))
        {
          list($boundary, $data) = $this->build_multipart_form_data($postfields);
          curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: multipart/form-data; boundary=$boundary"));
          curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
          print_r($data);
        }
      break;
      */
      case 'DELETE':
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
      break;
    }
    
    # executes the request
    $output = curl_exec($ch);
    
    if ($output === false)
    {
      echo "\n".Terminal::colorize('cURL error:', 'LIGHT_RED').' '.Terminal::colorize(curl_error($ch), 'RED ')."\n\n";
      die("Maybe you didn't started a test server?\n\$ script/server -e test -p 3009\n\n");
    }
    
    # parses headers
    $headers = array('cookies' => array());
    foreach(explode("\n", trim(substr($output, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE)))) as $line)
    {
      if (strpos($line, ':'))
      {
        list($header, $value) = explode(':', trim($line), 2);
        $header = strtolower($header);
        if ($header == 'set-cookie')
        {
          preg_match('/^\s*([^=]+)=([^;]+)/', $value, $match);
          $headers['cookies'][$match[1]] = $match[2];
        }
        $headers[$header] = trim($value);
      }
    }
    
    # gets additional informations
    $this->last_action = array(
      'url'        => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
      'method'     => $method,
      'postfields' => $postfields,
      'status'     => curl_getinfo($ch, CURLINFO_HTTP_CODE),
      'headers'    => $headers,
      'body'       => trim(substr($output, curl_getinfo($ch, CURLINFO_HEADER_SIZE))),
    );
    
    curl_close($ch);
    return $this->last_action;
  }
  
  private function & flatten_postfields(&$postfields)
  {
    $data = array();
    $fields = explode('&', http_build_query($postfields));
    
    foreach($fields as $v)
    {
      if (!empty($v))
      {
        list($k, $v) = explode('=', $v, 2);
        $data[urldecode($k)] = urldecode($v);
      }
    }
    return $data;
  }
  /*
  private function build_multipart_form_data(&$postfields)
  {
    $data = "";
    $boundary = "---------------------".substr(md5(rand(0,32000)), 0, 10);
    
    foreach($postfields as $k => $v)
    {
      if (strpos(ltrim($v), '@') === 0)
      {
        $data .= "Content-Disposition: form-data; name=\"{$k}\"; filename=\"{$v}\"\n"; 
        $data .= "Content-Type: ".mime_content_type($v)."\n"; 
        $data .= "Content-Transfer-Encoding: binary\n\n"; 
        $data .= file_get_contents($v)."\n"; 
        $data .= "--$boundary--\n"; 
      }
{'fraction_non_alphanumeric': 0.18882100396301188, 'fraction_numerical': 0.005614266842800529, 'mean_word_length': 3.0255898969757395, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7193310', 'n_tokens_mistral': 4203, 'n_tokens_neox': 4085, 'n_words': 689}