Tester.php
<?php
namespace Tlf\Lexer\Test;
class Tester extends \Tlf\Tester {
/**
* you pass an array like this to runDirectiveTest($grammars, $thingies)
*
*/
protected $sample_thingies = [
// name of test =>
'Values.CloseArgList'=>[
// starting ast
'ast.type'=>'var_assign',
// starting directive
'start'=>['php_code'],
// string to lexify
'input'=>'"This")',
// ast tree to expect
'expect'=>[
'value'=>'"This"',
'declaration'=>'"This"',
],
],
'Docblock.OneLine'=>[
// starting directive
'start'=>['/*'],
// string to lexify
'input'=>"/* abc */",
// check lexer->previous() for each key=>value()
'expect.previous'=>[
// `$lexer->previous('docblock') must == ['type'=>docblock,description=>abc]`
"docblock"=> [
'type'=>'docblock',
'description'=>'abc',
],
],
],
];
/**
* See above sample
*
* @param $grammars an array of grammrs to run on the directives
* @param $thingies an array of directive tests
*
* @return an array of test results where key is test name & value is true/false for pass/fail
*/
protected function runDirectiveTests($grammars, $thingies){
$skipped = [];
/** test descriptions that failed to pass*/
$failures = [];
$test_results = [];
$success_count = 0;
foreach ($thingies as $description=>$test){
$startingAst = $test['ast']??null;
if ($startingAst==null
&&isset($test['ast.type'])
){
$startingAst = ['type'=>$test['ast.type']];
}
$startingDirectives=$test['start'];
if (!is_array($startingDirectives))$startingDirectives = [$startingDirectives];
$inputString=$test['input']??file_get_contents($test['file']);
// a sloppy way to add alternate expectations based upon 'previous'
$expect=$test['expect'] ?? $test['expect.previous'];
$compareTo = isset($test['expect']) ? 'tree' : 'previous';
$run = $this->options['run']??null;
if ($run===null||$run==$description
|| substr($run,-1)=='*'
&& substr($description, 0, (strlen($run) - 1) )
== substr($run,0,-1)
){
$this->test($description);
$lexer = new \Tlf\Lexer();
if (isset($this->options['version'])){
$lexer->version = (float)$this->options['version'];
} else {
$lexer->version = \Tlf\Lexer\Versions::_1;
}
if (isset($this->options['stop_loop'])){
$lexer->stop_loop = $this->options['stop_loop'];
}
foreach ($grammars as $g){
$lexer->addGrammar($g, null, false);
}
$lexer->addGrammar($grammars[0], 'this', false);
if (isset($this->options['run']) && substr($run,-1)!='*')$lexer->debug = true;
$tree = $this->parse($lexer, $inputString, $startingDirectives, $startingAst);
if (($test['expect_failure']??false)===true){
$this->invert();
echo "\n # This grammar feature isn't implemented yet";
}
if (isset($this->options['run']) && isset($this->options['print_full'])){
print_r($tree);
echo "\n\n\n";
exit;
}
// more of the sloppy way to add alternate expectations
if ($compareTo=='tree'){
$succeeded = $this->compare($expect, $tree);
if (!$succeeded){
$failures[] = $description;
}
} else if ($compareTo=='previous'){
foreach ($expect as $key=>$target){
$previous = $lexer->previous($key);
if ($previous instanceof \Tlf\Lexer\Ast){
$tree = $previous->getTree();
} else $tree = $previous;
$succeeded = $this->compare($target, $tree);
if (!$succeeded){
$failures[] = $description;
}
// $this->compare(var_export($target,true), var_export($tree, true));
}
}
if ($succeeded)$success_count++;
if (($test['expect_failure']??false)===true){
$this->invert();
}
if (($test['is_bad_test']??false)!==false){
echo " \033[4;31m"."# BAD SUBTEST"."\033[0m\n";
echo ' '.$test['is_bad_test'];
echo "\n";
// echo "\n\n---------badd test -------------\n\n";
}
$test_results[$description] = $succeeded;
} else {
$skipped[] = $description;
}
}
echo "\n\n";
$skipCount = count($skipped);
if ($skipCount>0){
echo "$skipCount sub tests were skipped: ". implode(", ", $skipped)."\n";
}
$failedCount = count($failures);
if ($failedCount>0){
echo "\n$failedCount sub tests failed: ". implode(", ", $failures)."\n";
}
echo "\n";
echo "\n$success_count subtests passed";
echo "\n\n";
echo "Specify `-run \"Test Description\"` to only run what you need";
echo "\nSpecify `-print_full` print the full ast result when using `-run`";
return $test_results;
}
/**
* parse input and return an ast tree (without the ast type & the source)
* @param $lexer a lexer instance
* @param $toLex the text to lex
* @param $startingDirectives an array of starting directives like `['php_code']`
* @param $startingAst (optional) an array like `'type'=>'some_type'`
*/
protected function parse(\Tlf\Lexer $lexer, string $toLex, array $startingDirectives, ?array $startingAst){
foreach ($startingDirectives as $name){
$parts = explode(':',$name);
$namespace = count($parts)===2 ? $parts[0] : 'this';
$grammar = $lexer->getGrammar($namespace);
foreach ($grammar->getDirectives(':'.$name) as $directive){
$lexer->addDirective($directive);
}
}
if ($startingAst!=null){
$startingAst = new \Tlf\Lexer\Ast($startingAst['type'], $startingAst);
}
$ast = $lexer->lex($toLex,$startingAst);
$tree = $ast->getTree();
unset($tree['type'], $tree['src']);
return $tree;
}
}