Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
96ef72c71c | ||
|
9b2546c3ee | ||
|
c309f2b225 | ||
|
e2e428b28c | ||
|
5289eaedaf | ||
|
4ae0e04391 | ||
|
b75e3af1b5 | ||
|
ce7c917874 | ||
|
0ce38258dc | ||
|
8f41a5583b | ||
|
e8ef5f535c | ||
|
a5be144041 | ||
|
30db5377eb | ||
|
b2c3faf5c1 | ||
|
811f953e6e | ||
|
afc4c618f4 | ||
|
1d2ca239e7 | ||
|
05d4e2e3c1 | ||
|
7fada2c155 | ||
920b8a3cc1 | |||
|
71cffd1614 | ||
|
994768ca9d | ||
|
1cbcac0361 | ||
|
bcea50c5dd | ||
|
0c98200fc2 | ||
|
e4c9d97697 | ||
|
495d4e0d26 | ||
6c0873fc6f | |||
cc0eec3b4a | |||
|
676c2215c7 | ||
|
4ecd985aba |
18
README.md
18
README.md
@ -1 +1,19 @@
|
|||||||
Post PHPUnit test results to Gitea
|
Post PHPUnit test results to Gitea
|
||||||
|
|
||||||
|
Enable in phpunit.xml with
|
||||||
|
|
||||||
|
```plain
|
||||||
|
<phpunit ... printerClass="JHodges\GiteaBotPHPUnit\ResultPrinter">
|
||||||
|
|
||||||
|
<php>
|
||||||
|
<env name="GiteaUrl" value="https://try.gitea.io/api/v1/"/>
|
||||||
|
<env name="GiteaUser" value="bot"/>
|
||||||
|
<!--env name="GiteaPass" value="xxx"/--> <!--probably set this on the machine env-->
|
||||||
|
<env name="GiteaRepoUser" value="bobemoe"/>
|
||||||
|
<env name="GiteaRepo" value="test"/>
|
||||||
|
</php>
|
||||||
|
|
||||||
|
<logging>
|
||||||
|
<log type="junit" target="/tmp/logfile.xml"/>
|
||||||
|
</logging>
|
||||||
|
```
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"jhodges/giteabot": "~1.2.0"
|
"jhodges/giteabot": "dev-master"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
160
src/PostFailedTests.php
Normal file
160
src/PostFailedTests.php
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
namespace JHodges\GiteaBotPHPUnit;
|
||||||
|
|
||||||
|
use \JHodges\GiteaBot\Client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a new issue for each failed test
|
||||||
|
* if it already exists then report is added as a comment
|
||||||
|
* if exact error exists as body or comment then just link to it instead of posting again
|
||||||
|
* if it passes then the issue is closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PostFailedTests{
|
||||||
|
|
||||||
|
private $repo=null;
|
||||||
|
private $xml=null;
|
||||||
|
|
||||||
|
function __construct($xml_path){
|
||||||
|
$this->xml = simplexml_load_file($xml_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post($url, $user, $pass, $repoUser, $repo){
|
||||||
|
if(!$url) return;
|
||||||
|
|
||||||
|
// open connection and repo
|
||||||
|
$client=new Client($url, $user, $pass, true);
|
||||||
|
$this->repo=$client->getRepo($repoUser, $repo);
|
||||||
|
|
||||||
|
$this->process($this->xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process($item){
|
||||||
|
if(isset($item->testcase)){
|
||||||
|
foreach($item->testcase as $testcase) {
|
||||||
|
$att = current($testcase->attributes());
|
||||||
|
$name=$att['class'].'::'.$att['name'];
|
||||||
|
$fail=false;
|
||||||
|
foreach($testcase as $k=>$v){
|
||||||
|
$this->fail($name,$att,$k,$v);
|
||||||
|
$fail=true;
|
||||||
|
}
|
||||||
|
if(!$fail){
|
||||||
|
$this->success($name,$att);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isset($item->testsuite)){
|
||||||
|
foreach($item->testsuite as $testsuite) {
|
||||||
|
$this->process($testsuite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function success($name,$att){
|
||||||
|
$issue=$this->repo->getIssues(['q' => $name])[0]??null;
|
||||||
|
|
||||||
|
if($issue){
|
||||||
|
$issue->state='closed';
|
||||||
|
$issue->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fail($name,$att,$error,$message){
|
||||||
|
//stick the error type into the atts array
|
||||||
|
$att['result']=$error;
|
||||||
|
//trim the message
|
||||||
|
$message=trim($message);
|
||||||
|
|
||||||
|
// get any existing issue
|
||||||
|
$issue=$this->repo->getIssues(['q' => $name])[0]??null;
|
||||||
|
|
||||||
|
// if existing issue.
|
||||||
|
if($issue){
|
||||||
|
// are there any instructions in comments?
|
||||||
|
$quiet=$silent=false;
|
||||||
|
foreach($issue->getComments() as $comment){
|
||||||
|
if( stripos('@bot quiet',$comment->body)!==false ){
|
||||||
|
$quiet=true;
|
||||||
|
$silent=false;
|
||||||
|
}
|
||||||
|
if( stripos('@bot silent',$comment->body)!==false ){
|
||||||
|
$silent=true;
|
||||||
|
$quiet=true;
|
||||||
|
}
|
||||||
|
if( stripos('@bot normal',$comment->body)!==false ){
|
||||||
|
$silent=false;
|
||||||
|
$quiet=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($silent){return;}
|
||||||
|
|
||||||
|
//is the error the same?
|
||||||
|
if( $this->doesMessageMatch($issue->body,$message,$att) ){
|
||||||
|
if($quiet){return;}
|
||||||
|
$issue->addComment("Failed again. Same errors as [above]($issue->html_url#issue-{$issue->id})");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
foreach($issue->getComments() as $comment){
|
||||||
|
if( $this->doesMessageMatch($comment->body,$message,$att) ){
|
||||||
|
if($quiet){return;}
|
||||||
|
$issue->addComment("Failed again. Same errors as [above]($issue->html_url#issuecomment-{$comment->id})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//so its a new issue or changed error, lets upload screenshot and compose the body
|
||||||
|
$issueBody='';
|
||||||
|
if(file_exists("/tmp/$name.png")){
|
||||||
|
$data=$this->repo->addReleaseAttachment(1,"/tmp/$name.png");
|
||||||
|
$url=$data->browser_download_url;
|
||||||
|
$issueBody.="\n\n";
|
||||||
|
unlink("/tmp/$name.png");
|
||||||
|
}
|
||||||
|
if($message) $issueBody.="```plain\n$message\n```\n";
|
||||||
|
foreach($att as $k=>$v){
|
||||||
|
$issueBody.=" * $k = $v\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//post the body
|
||||||
|
if($issue){
|
||||||
|
$issue->addComment($issueBody);
|
||||||
|
}else{
|
||||||
|
$issue=$this->repo->createIssue([
|
||||||
|
'title'=>'Failed test '.$name,
|
||||||
|
'body'=>$issueBody
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function doesMessageMatch($body,$message,$att){
|
||||||
|
// extract the old message
|
||||||
|
preg_match('#```plain\n(.*)\n```#s',$body,$match);
|
||||||
|
$messageOld=$match[1]??'';
|
||||||
|
// return false if not the same
|
||||||
|
if( $message != $messageOld ){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the old atts
|
||||||
|
$attOld=[];
|
||||||
|
preg_match_all('# \* (.*?) = (.*)#',$body,$m);
|
||||||
|
foreach($m[0]??[] as $k=>$v){
|
||||||
|
$attOld[$m[1][$k]]=$m[2][$k];
|
||||||
|
}
|
||||||
|
// make sure all current attributes (except time) are the same as old
|
||||||
|
// return false on first mis-match
|
||||||
|
foreach($att as $k=>$v){
|
||||||
|
if($k=='time') continue;
|
||||||
|
if(($attOld[$k]??null) != $v){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all matches, error message is the same
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
80
src/PostFullReport.php
Normal file
80
src/PostFullReport.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
namespace JHodges\GiteaBotPHPUnit;
|
||||||
|
|
||||||
|
use \JHodges\GiteaBot\Client;
|
||||||
|
|
||||||
|
final class PostFullReport{
|
||||||
|
|
||||||
|
private $report;
|
||||||
|
private $level=0;
|
||||||
|
private $failCount=0;
|
||||||
|
|
||||||
|
function __construct($xml_path){
|
||||||
|
$this->xml = simplexml_load_file($xml_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function post($url, $user, $pass, $repoUser, $repo){
|
||||||
|
if(!$url) return;
|
||||||
|
// open connection and repo
|
||||||
|
$this->client=new Client($url, $user, $pass);
|
||||||
|
$this->repo=$this->client->getRepo($repoUser, $repo);
|
||||||
|
|
||||||
|
$this->process($this->xml); //process xml and upload screenshots
|
||||||
|
// create the issue
|
||||||
|
if(trim($this->report)){
|
||||||
|
$issue=$this->repo->createIssue([
|
||||||
|
'title'=>'Test Results',
|
||||||
|
'body'=>$this->report
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFailCount(){
|
||||||
|
return $this->failCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function process($item){
|
||||||
|
$this->level++;
|
||||||
|
if(isset($item->testcase)){
|
||||||
|
foreach($item->testcase as $testcase) {
|
||||||
|
$att=$testcase->attributes();
|
||||||
|
$failmsg='';
|
||||||
|
foreach($testcase as $k=>$v){
|
||||||
|
$failmsg="(**$k**)\n";
|
||||||
|
if(trim($v)) $failmsg.="```plain\n$v\n```\n";
|
||||||
|
}
|
||||||
|
if($failmsg){
|
||||||
|
$this->failCount++;
|
||||||
|
$this->report.=" * [ ] {$att->name[0]} $failmsg\n";
|
||||||
|
}else{
|
||||||
|
$this->report.=" * [x] {$att->name[0]}\n";
|
||||||
|
}
|
||||||
|
$name=$testcase->attributes()->class[0].'::'.$testcase->attributes()->name[0];
|
||||||
|
if(file_exists("/tmp/$name.png")){
|
||||||
|
$data=$this->repo->addReleaseAttachment(1,"/tmp/$name.png");
|
||||||
|
$url=$data->browser_download_url;
|
||||||
|
$img="\n\n";
|
||||||
|
unlink("/tmp/$name.png");
|
||||||
|
$this->report.="$img\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isset($item->testsuite)){
|
||||||
|
foreach($item->testsuite as $testsuite) {
|
||||||
|
$att=$testsuite->attributes();
|
||||||
|
$this->report.=str_repeat('#',$this->level)." {$att->name[0]}\n".
|
||||||
|
"tests: {$att->tests[0]}".
|
||||||
|
", assertions: {$att->assertions[0]}".
|
||||||
|
", errors: {$att->errors[0]}".
|
||||||
|
", failures: {$att->failures[0]}".
|
||||||
|
", skipped: {$att->skipped[0]}".
|
||||||
|
", time: {$att->time[0]}".
|
||||||
|
"\n";
|
||||||
|
$this->process($testsuite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->level--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace JHodges\GiteaBotPHPUnit;
|
|
||||||
|
|
||||||
use \JHodges\GiteaBot\Client;
|
|
||||||
|
|
||||||
final class Poster{
|
|
||||||
|
|
||||||
private $report;
|
|
||||||
|
|
||||||
function __construct($testdox_path,$xml_path){
|
|
||||||
$testdox=file_get_contents($testdox_path);
|
|
||||||
$testdox=preg_replace('# \[#',' * [',$testdox);
|
|
||||||
|
|
||||||
$this->report.=$testdox;
|
|
||||||
|
|
||||||
$xml = simplexml_load_file($xml_path);
|
|
||||||
$this->process($xml);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function post($url, $user, $pass, $repoUser, $repo){
|
|
||||||
// open connection and repo
|
|
||||||
$client=new Client($url, $user, $pass, $repoUser, $repo);
|
|
||||||
$repo=$client->getRepo($repoUser, $repo);
|
|
||||||
|
|
||||||
// create the issue
|
|
||||||
$issue=$repo->createIssue([
|
|
||||||
'title'=>'Test Results',
|
|
||||||
'body'=>$this->report
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private function process($item){
|
|
||||||
if(isset($item->testcase)){
|
|
||||||
foreach($item->testcase as $testcase) {
|
|
||||||
foreach($testcase as $k=>$v){
|
|
||||||
$this->report.="\n```plain\n".print_r($testcase,true)."\n```";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(isset($item->testsuite)){
|
|
||||||
foreach($item->testsuite as $testsuite) {
|
|
||||||
$this->process($testsuite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,34 +1,34 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace JHodges\GiteaBotPHPUnit;
|
namespace JHodges\GiteaBotPHPUnit;
|
||||||
|
|
||||||
class ResultPrinter extends \PHPUnit\TextUI\ResultPrinter
|
class ResultPrinter extends \PHPUnit\TextUI\ResultPrinter{
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80){
|
public function __construct($out = null, $verbose = false, $colors = self::COLOR_DEFAULT, $debug = false, $numberOfColumns = 80){
|
||||||
parent::__construct($out, $verbose, $colors , $debug , $numberOfColumns);
|
parent::__construct($out, $verbose, $colors , $debug , $numberOfColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function printResult(\PHPUnit\Framework\TestResult $result)
|
public function printResult(\PHPUnit\Framework\TestResult $result){
|
||||||
{
|
parent::printResult($result);
|
||||||
$poster=new \JHodges\GiteaBotPHPUnit\Poster('/tmp/testdox.txt','/tmp/logfile.xml');
|
$poster=new \JHodges\GiteaBotPHPUnit\PostFailedTests('/tmp/logfile.xml');
|
||||||
$poster->post(
|
$poster->post(
|
||||||
getenv('GiteaUrl'),
|
getenv('GiteaUrl'),
|
||||||
getenv('GiteaUser'),
|
getenv('GiteaUser'),
|
||||||
getenv('GiteaPass'),
|
getenv('GiteaPass'),
|
||||||
getenv('GiteaRepoUser'),
|
getenv('GiteaRepoUser'),
|
||||||
getenv('GiteaRepo')
|
getenv('GiteaRepo')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function printHeader()
|
protected function printHeader(){
|
||||||
{
|
parent::printHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function messageProcessor(array $record)
|
public function messageProcessor(array $record){
|
||||||
{
|
parent::messageProcessor($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function suiteNameProcessor(array $record)
|
public function suiteNameProcessor(array $record){
|
||||||
{
|
parent::suiteNameProcessor($record);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user