#!/usr/bin/php -q
<?

   /*
function vardump($obj) {
  $buf="";
  if (is_array($obj)) {
    $comma=0;
    $buf.="array(";
    foreach($obj as $key=>$val) {
      if ($comma++)
	$buf.=",";
      $buf.='"'.$key.'"'."=>";
      $buf.=vardump($val);
    }
    $buf.=")";
  } else if (is_null($obj)) {
    $buf.="null";
  } else if (is_bool($obj)) {
    if ($obj===true)
      $buf.="true";
    else
      $buf.="false";
  } else if (is_string($obj)) {
    $buf.='"'.$obj.'"';
  } else if (is_numeric($obj)) {
    $buf.=$obj;
  } else if (is_object($obj)) {
    $comma=0;
    $buf.="new ".get_class($obj);
  } else if (is_resource($obj)) {
    $buf.=sprintf("%s",$obj);
  }
  return($buf);
}
   */

/*
level 5: (no reads/writes) requires code installed in a place the cli.htp will work if needed
level 4: (no writes) requires data files and database are setup. preform no writes
level 3: can preform writes
level 2: requires the web server is running
level 1: requires an headless web client
*/

/* function sqtest_getfnctlist_parse_t0001_doc() { */
/*   $doc["short"]="simple one function"; */
/*   printf("%s\n",json_encode($doc)); */
/* } */

if (!$GLOBALS["_SQTEST_"]) {

  function sqtest_getfnctlist_parse(&$fnctlist,$phpcode,$file) {
    $tokens=token_get_all($phpcode);
    foreach($tokens as $key=>$token) {
      if (is_array($token)) {	
	if ($token[1]=="function" && $tokens[$key+3]=="(") {
	  $sqtestvars=[];
	  $fnct=$tokens[$key+2][1];
/* 	  $fnctlist["fulllookup"][$fnct]=true; */
	  if (is_numeric(substr($tokens[$key+2][1],-4))) {
	    $name=$tokens[$key+2][1];
	    if (substr($name,-6,2)=="_t") {
	      $level="5";
	      //} else if (substr($name,-8,2)=="_l" && substr($name,-5,1)=="t" && is_numeric(substr($name,-6,1))) {
	    } else if (substr($name,-8,2)=="_l" && substr($name,-5,1)=="t") {
	      $level=substr($name,-6,1);
	    } else {
	      continue;
	    }
	    //	    if (strpos($diaglevel,$level)!==false) {
	      $fnctlist["list"][$fnct]=array("fnct"=>$fnct, "file"=>$file, "level"=>$level);
	      //$fnctlist["lookup"][$fnct]=true;
	      //	    }
	  }
	} else {
	  $name=token_name($token[0]);
	  if ($name=="T_VARIABLE" && $token[1]=="\$SQTEST") {
	    $varmode="name";
	  } else if ($varmode=="name" && $name=="T_CONSTANT_ENCAPSED_STRING") {
	    $varname=$token[1];
	  } else if ($varmode=="=" && $name=="T_CONSTANT_ENCAPSED_STRING") {
	    $fnctlist["list"][$fnct]["value"][substr($varname,1,-1)]=substr($token[1],1,-1);
	  }
	}
      } else {
	if ($varmode && $token==";")
	  $varmode="";
	else if ($varmode=="name" && $token=="=")
	  $varmode="=";
      }
    }
    return($fnctlist);
  }

  function sqtest_getfnctlist_parse_t0001() {
    $phpcode=
      "<?\n".
      "function foobar_t0001() {\n".
      " printf('PASS')\n".
      "}\n".
      "?>";
    $fnctlist=array();
    sqtest_getfnctlist_parse($fnctlist,$phpcode,"/tmp/sqtest.htp");
    if ($fnctlist["list"]["foobar_t0001"]["fnct"]=="foobar_t0001" &&
	$fnctlist["list"]["foobar_t0001"]["file"]=="/tmp/sqtest.htp") {
      printf("PASS\n");
    } else {
      printf("FAIL\n");
    }
  }

/* function sqtest_getfnctlist_parse_t0002_doc() { */
/*   $doc["short"]="level 4 with 4 fncts"; */
/*   printf("%s\n",json_encode($doc)); */
/* } */

  function sqtest_getfnctlist_parse_t0002() {
    $phpcode=
      "<?\n".
      "function foobar_t0001() {\n".
      " printf('PASS')\n".
      "}\n".
      "function foobar_l2t0002() {\n".
      " printf('PASS')\n".
      "}\n".
      "function foobar_l4t0003() {\n".
      " printf('PASS')\n".
      "}\n".
      "function foobar_l3t0004() {\n".
      " printf('PASS')\n".
      "}\n".
      "?>";
    $fnctlist=array();
    sqtest_getfnctlist_parse($fnctlist,$phpcode,"/tmp/sqtest.htp","345");
    if ($fnctlist["list"]["foobar_t0001"]["fnct"]=="foobar_t0001" &&
	$fnctlist["list"]["foobar_l4t0003"]["fnct"]=="foobar_l4t0003" &&
	$fnctlist["list"]["foobar_l3t0004"]["fnct"]=="foobar_l3t0004" &&
	$fnctlist["list"]["foobar_t0001"]["file"]=="/tmp/sqtest.htp") {
      printf("PASS\n");
    } else {
      printf("FAIL\n");
    }
  }

  function sqtest_getfnctlist_parse_t0003() {
    $phpcode=
      "<?\n".
      "function foobar_t0001() {\n".
      " \$SQTEST[\"name\"]=\"Testing Function\";".
      " printf('PASS')\n".
      "}\n".
      "?>";
    $fnctlist=array();
    sqtest_getfnctlist_parse($fnctlist,$phpcode,"/tmp/sqtest.htp","345");
    if ($fnctlist["list"]["foobar_t0001"]==array("fnct"=>"foobar_t0001", "file"=>"/tmp/sqtest.htp", "value"=>array("name"=>"Testing Function"), "level"=>"5")) {
      printf("PASS\n");
    } else {
      printf("FAIL\n");
    }
  }

  function sqtest_getfnctlist_parse_t0004() {
    $phpcode=
      "<?\n".
      "function foobar_t0001() {\n".
      " \$SQTEST[\"name\"]=\"Testing Function\";".
      " \$a=4;\n".
      " \$SQTEST[\"desc\"]=\"Just a Test\";".
      " printf('PASS')\n".
      "}\n".
      "?>";
    $fnctlist=array();
    sqtest_getfnctlist_parse($fnctlist,$phpcode,"/tmp/sqtest.htp","345");
    if ($fnctlist["list"]["foobar_t0001"]==array("fnct"=>"foobar_t0001", "file"=>"/tmp/sqtest.htp", "value"=>array("name"=>"Testing Function", "desc"=>"Just a Test"), "level"=>"5")) {
      printf("PASS\n");
    } else {
      printf("FAIL\n");
    }
  }

  function sqtest_getfnctlist_parse_t0005() {
    $phpcode=
      "<?\n".
      "function foobar_l4t0005() {\n".
      " printf('PASS')\n".
      "}\n".
      "?>";
    $fnctlist=array();
    sqtest_getfnctlist_parse($fnctlist,$phpcode,"/tmp/sqtest.htp","345");
    if ($fnctlist["list"]["foobar_l4t0005"] ==
	array("fnct"=>"foobar_l4t0005", "file"=>"/tmp/sqtest.htp", "level"=>"4")) {
      printf("PASS\n");
    } else {
      printf("FAIL\n");
    }
  }

  function sqtest_getfnctlist(&$fnctlist,$file) {
    $phpcode=file_get_contents($file);
    sqtest_getfnctlist_parse($fnctlist,$phpcode,$file);
    return($fnctlist);
  }

  function sqtest_getfnctlist_recursive(&$fnctlist,$file) {
    if (is_dir($file)) {
      $dirname=$file;
      $dir=scandir($dirname);
      foreach($dir as $file) {
	$filename=$dirname."/".$file;
	if ($file=="." || $file=="..")
	  continue;
	if (is_file($filename) && substr($file,-4)!=".htp")
	  continue;
	sqtest_getfnctlist_recursive($fnctlist,$filename);
      }
    } else if (is_file($file) && substr($file,-4)==".htp") {
      sqtest_getfnctlist($fnctlist,$file);
    }
  }

  function sqtest_parseargs(&$args) {
    $arglist=array();
    for($n=1;$n<count($args);$n++) {
      $arg=$args[$n];
      if (substr($arg,0,1)=="-") {
	$opt=substr($arg,1);
	for($i=0;$i<strlen($opt);$i++) {
	  $letter=substr($opt,$i,1);
	  if ($letter=="l") {
	    $opts[$letter]=substr($opt,$i+1);
	    break;
	  } else if ($letter=="f") {
	    $opts[$letter][]=$args[$n+1];
	    $n++;
	  } else {
	    $opts[$letter]=true;
	  }
	}
      } else if (substr($arg,0,2)=="--") {
	$opt=substr($arg,2);
	$opts[$opt]=true;
      } else {
	$arglist[]=$arg;
      }
      if (is_numeric($arg) && sprintf("%d",$arg)==$arg) {
	$opts["e"]=true;
	$opts["v"]=true;
      }
    }
    $args=$arglist;
    return($opts);
  }

  function sqtest_parseargs_t0003() {
    $args=["sqtest","-v","base.test.htp"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["v"=>"v"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_t0004() {
    $args=["sqtest","-vl4","base.test.htp"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["v"=>"v","l"=>"4"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_t0005() {
    $args=["sqtest","-e","base.test.htp"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["e"=>"e"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_t0006() {
    $args=["sqtest","base.test.htp","-f","foobar_t0001"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["f"=>["foobar_t0001"]])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_t0013() {
    $args=["sqtest","base.test.htp","-vl45"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["v"=>"v", "l"=>"45"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_t0014() {
    $args=["sqtest","-lm","base.test.htp"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["l"=>"m"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_parseargs_lmt0015() {
    $args=["sqtest","-lm","base.test.htp"];
    $opts=sqtest_parseargs($args);
    if ($args==["base.test.htp"] && $opts==["l"=>"m"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }
  
  function sqtest_filterfunctions(&$fnctlist,$limitlist) {
    $filterlist=array();
    foreach($fnctlist["list"] as $key=>$fnctdef) {
      foreach($limitlist as $limit) {
	if ((is_numeric($limit) && substr($fnctdef["fnct"],-4)==$limit) ||
	    $limit==$fnctdef["fnct"]) {
	  $filterlist[$key]=$fnctdef;
	  break;
	}
      }
    }
    $fnctlist["list"]=$filterlist;
  }

  function sqtest_filterfunctions_t0007() {
    $fnctlist=
      array("list"=>
	    array(
		  "foobar_t0001"=>array("fnct"=>"foobar_t0001", "level"=>"5", "file"=>"/tmp/foobar.htp"),
		  "foobar_l4t0002"=>array("fnct"=>"foobar_l4t0002", "level"=>"4", "file"=>"/tmp/foobar.htp"),
		  ),
	    );
    $limitlist=["foobar_t0001"];
    sqtest_filterfunctions($fnctlist,$limitlist);
    if ($fnctlist["list"]=array("foobar_t0001"=>array("fnct"=>"foobar_t0001", "level"=>"5", "file"=>"/tmp/foobar.htp")))
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_filterfunctions_t0008() {
    $fnctlist=
      array("list"=>
	    array(
		  "foobar_t0001"=>array("fnct"=>"foobar_t0001", "level"=>"5", "file"=>"/tmp/foobar.htp"),
		  "foobar_l4t0002"=>array("fnct"=>"foobar_l4t0002", "level"=>"4", "file"=>"/tmp/foobar.htp"),
		  ),
	    );
    $limitlist=["2"];
    sqtest_filterfunctions($fnctlist,$limitlist);
    if ($fnctlist["list"]=array("foobar_l4t0002"=>array("fnct"=>"foobar_l4t0002", "level"=>"4", "file"=>"/tmp/foobar.htp")))
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_sortfncts_recur(&$fnctlist,$fnctkey) {
    if ($fnctlist["sorted"][$fnctkey]) {
      return;
    }
    //    printf("mark274(%s)\n",$fnctkey);
    $fnctlist["sorted"][$fnctkey]=$fnctlist["list"][$fnctkey];
    if (is_array($fnctlist["requires"][$fnctkey])) {
      foreach($fnctlist["requires"][$fnctkey] as $key=>$fnctdef) {
	sqtest_sortfncts_recur($fnctlist,$key);
      }
    }
  }
  
  function sqtest_sortfncts(&$fnctlist) {
    foreach($fnctlist["list"] as $key=>$fnctdef) {
      if ($fnctdef["require"])
	$fnctlist["requires"][$fnctdef["require"]][$key]=$key;
    }
    foreach($fnctlist["list"] as $key=>$fnctdef) {
      if (!$fnctdef["require"] || $fnctlist["sorted"][$fnctdef["require"]]) {
	sqtest_sortfncts_recur($fnctlist,$key);
      }
    }
    if (count($fnctlist["list"])!=count($fnctlist["sorted"]))
      return(false);
    $fnctlist["list"]=$fnctlist["sorted"];
    return(true);
  }
  
  function sqtest_sortfncts_t0009() {
    $fnctlist["list"]=
      array(
	    "sortfnct_t0001"=>array("name"=>"sortfnct_t0001"),
	    "sortfnct_t0002"=>array("name"=>"sortfnct_t0002"),
	    "sortfnct_t0003"=>array("name"=>"sortfnct_t0003", "require"=>"sortfnct_t0004"),
	    "sortfnct_t0004"=>array("name"=>"sortfnct_t0004", "require"=>"sortfnct_t0002"),
	    "sortfnct_t0005"=>array("name"=>"sortfnct_t0005"),
	    "sortfnct_t0006"=>array("name"=>"sortfnct_t0006", "require"=>"sortfnct_t0002"));
    sqtest_sortfncts($fnctlist);
    if (array_keys($fnctlist["sorted"])==["sortfnct_t0001","sortfnct_t0002","sortfnct_t0004","sortfnct_t0003","sortfnct_t0006","sortfnct_t0005"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_sortfncts_t0010() {
    $fnctlist["list"]=
      array(
	    "sortfnct_t0001"=>array("name"=>"sortfnct_t0001", "require"=>"sortfnct_t0006"),
	    "sortfnct_t0002"=>array("name"=>"sortfnct_t0002", "require"=>"sortfnct_t0004"),
	    "sortfnct_t0003"=>array("name"=>"sortfnct_t0003", "require"=>"sortfnct_t0005"),
	    "sortfnct_t0004"=>array("name"=>"sortfnct_t0004"),
	    "sortfnct_t0005"=>array("name"=>"sortfnct_t0005"),
	    "sortfnct_t0006"=>array("name"=>"sortfnct_t0006", "require"=>"sortfnct_t0002"));
    sqtest_sortfncts($fnctlist);
    if (array_keys($fnctlist["sorted"])==["sortfnct_t0004","sortfnct_t0002","sortfnct_t0006","sortfnct_t0001","sortfnct_t0005","sortfnct_t0003"])
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_sortfncts_t0011() {
    $fnctlist["list"]=
      array(
	    "sortfnct_t0001"=>array("name"=>"sortfnct_t0001"),
	    "sortfnct_t0002"=>array("name"=>"sortfnct_t0002", "require"=>"sortfnct_t0003"),
	    "sortfnct_t0003"=>array("name"=>"sortfnct_t0003", "require"=>"sortfnct_t0002"),
	    "sortfnct_t0004"=>array("name"=>"sortfnct_t0004"),
	    "sortfnct_t0005"=>array("name"=>"sortfnct_t0005"),
	    "sortfnct_t0006"=>array("name"=>"sortfnct_t0006"));
    $reply=sqtest_sortfncts($fnctlist);
    if ($reply===false)
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_sortfncts_errors($fnctlist) {
    $buf="";
    $comma=0;
    foreach($fnctlist["list"] as $key=>$fnctdef) {
      if ($fnctlist["sorted"][$key])
	continue;
      if ($comma++)
	$buf.=", ";
      $buf.=$key;
    }
    return($buf);
  }
  
  function sqtest_sortfncts_errors_t0012() {
    $SQTEST["label"]="looping requirements";
    $fnctlist["list"]=
      array(
	    "foobar_t0001"=>array("name"=>"foobar_t0001"),
	    "foobar_t0002"=>array("name"=>"foobar_t0002", "require"=>"foobar_t0003"),
	    "foobar_t0003"=>array("name"=>"foobar_t0003", "require"=>"foobar_t0002"),
	    "foobar_t0004"=>array("name"=>"foobar_t0004"));
    sqtest_sortfncts($fnctlist);
    $reply=sqtest_sortfncts_errors($fnctlist);
    if ($reply=="foobar_t0002, foobar_t0003")
      printf("PASS\n");
    else
      printf("FAIL\n");
  }

  function sqtest_fnctdef_start_popen($fnctdef) {
    $execsh=$fnctdef["execscript"];
    $fnct=$fnctdef["fnct"];
    $file=basename($fnctdef["file"]);
    $dir=dirname($fnctdef["file"]);
    if ($dir) {
      $cmd=sprintf("cd %s ; %s %s -n -l%s -f %s 2>&1",$dir,$execsh,$file,$fnctdef["level"],$fnct);
    } else {
      $cmd=sprintf("%s %s -l%s -n -f %s 2>&1",$execsh,$file,$fnctdef["level"],$fnct);
    }
    $p=popen($cmd,"r");
    $output="";
    while($buf=fgets($p)) {
      $output.=$buf;
    }
    pclose($p);
    return($output);
  }
  
  function sqtest_fnctdef_loadexec($fnctdef) {
    static $cliloaded;
    $pwd=$fnctdef["pwd"];
    $rootdir=$pwd;
    if (!$cliloaded[$rootdir]) {
      while($rootdir!="" && $rootdir!="/") {
	if (is_file($rootdir."/cli.htp"))
	  break;
	$rootdir=dirname($rootdir);
      }
      if (!$rootdir || $rootdir=="/") {
	$rootdir="";
      }
      if ($rootdir)
	include_once($rootdir."/cli.htp");
      $cliloaded[$pwd]=true;
    }
    $GLOBALS["_SQTEST_"]=true;
    include_once($fnctdef["file"]);
    $fnct=$fnctdef["fnct"];
    if (function_exists($fnct)) {
      $fnct();
    } else {
      printf("Error: Function '%s' does not exists.\n",$fnct);
    }
  }
  
  function sqtest_fnctdef_start_fork($fnctdef) {
    $timeout=10;
    $output="";
    $socket=array();
    socket_create_pair(AF_UNIX,SOCK_STREAM,0,$socket);
    if ($pid=pcntl_fork()) {
      socket_close($socket[0]);
      for($i=0;$i<$timeout;$i++) {
	$read=array($socket[1]);
	$write=NULL;
	$except=NULL;
	$reads=socket_select($read,$write,$except,1);
	if ($reads===false)
	  break;
	if ($reads===0)
	  continue;
	$buf=@socket_read($socket[1],8192,PHP_NORMAL_READ);
	if ($buf===false) {
	  pcntl_waitpid($pid,$status,0,$resources);
	  break;
	}
	$output.=$buf;
      }
      if ($i>=$timeout) {
	posix_kill($pid,9);
	$output.="FAIL\nexecution timeout exceeded\n";
      }
      socket_close($socket[1]);
      return($output);
    } else {
      socket_close($socket[1]);
      ob_start();
      $fnctdef["pwd"]=getenv("PWD");
      sqtest_fnctdef_loadexec($fnctdef);
      $buf=ob_get_clean();
      socket_write($socket[0],$buf,strlen($buf));
      exit;
    }
  }
  
  function sqtest_fnctdef_start($fnctdef) {
    if (function_exists("pcntl_fork") && function_exists("socket_select"))
      return(sqtest_fnctdef_start_fork($fnctdef));
    else
      return(sqtest_fnctdef_start_popen($fnctdef));
  }
  
  function sqtest($args) {
    if (substr($execsh=$args[0],0,1)!="/") {
      $execsh=realpath(getenv("PWD")."/".$execsh);
    }
    $opts=sqtest_parseargs($args);

    if (!($diaglevel=sprintf("%s",$opts["l"]))) {
      $diaglevel="5";
    }

    $pwd=getenv("PWD");
    $fnctlist=array("list"=>array());
    foreach($args as $arg) {
      sqtest_getfnctlist_recursive($fnctlist,$pwd."/".$arg);
    }

    $diaglist=array();
    foreach($fnctlist["list"] as $key=>$fnctdef) {
      if (strpos($diaglevel,$fnctdef["level"])!==false) {
	$diaglist[$key]=$fnctdef;
      }
    }
    if (!sqtest_sortfncts($fnctlist)) {
      printf("Errors: Requirement are incorrect for the following functions\n");
      printf("%s\n",sqtest_sortfncts_errors($fnctlist));
      return;
    }
    $fnctlist["list"]=$diaglist;
    if ($opts["f"]) {
      sqtest_filterfunctions($fnctlist,$opts["f"]);
    }

    if (!count($fnctlist["list"])) {
      printf("Error: no functions found\n",$fnct);
    }
    
    if ($opts["n"]) {
      foreach($fnctlist["list"] as $fnctdef) {
	$fnctdef["pwd"]=$pwd;
	sqtest_fnctdef_loadexec($fnctdef);
      }
      return;
      $rootdir=$pwd;
      while($rootdir!="" && $rootdir!="/") {
	if (is_file($rootdir."/cli.htp"))
	  break;
	$rootdir=dirname($rootdir);
      }
      if (!$rootdir || $rootdir=="/") {
	$rootdir="";
      }
      if ($rootdir)
	include_once($rootdir."/cli.htp");
      $GLOBALS["_SQTEST_"]=true;
      foreach($fnctlist["list"] as $fnctdef) {
	include_once($fnctdef["file"]);
	$fnct=$fnctdef["fnct"];
	if (function_exists($fnct)) {
	  $fnct();
	} else {
	  printf("Error: Function '%s' does not exists.\n",$fnct);
	}
      }
      return;
    }
    
    $counter=0;
    foreach($fnctlist["list"] as $fnctdef) {
      $counter++;
      $fnctdef["execscript"]=$execsh;
      $output=sqtest_fnctdef_start($fnctdef,$execsh);
      printf("%d/%d ",$counter,count($fnctlist["list"]));
      if (substr($output,0,3)=="#!/")
	$output=trim(substr($output,strpos($output,"\n")));
      if (trim($output)=="PASS") {
	printf("PASS %s %s\n",$fnctdef["fnct"],$fnctdef["value"]["label"]);
      } else {
	$fails[]=$fnctdef;
	printf("FAIL %s\n",$fnctdef["fnct"]);
	if ($opts["v"])
	  echo $output;
      }
    }
    if ($fails) {
      foreach($fails as $fail) {
	$filename=basename($fail["file"]);
	if ($dirname=substr(dirname($fail["file"]),strlen($pwd)+1))
	  $filename=$dirname."/".$filename;
	printf("FAIL! %s %s\n",$fail["fnct"],$filename);
      }
    }
  }
  sqtest($argv);
}

?>
